From 68b66a6f118304d962fdfa5de54709b1651eca41 Mon Sep 17 00:00:00 2001 From: elnafateh Date: Fri, 10 Jul 2026 07:35:34 +0100 Subject: [PATCH] FINERACT-2165: revert CommandProcessingResult --- .../jobs/api/SchedulerJobApiResource.java | 13 +- ...COBCatchUpInstanceModeIntegrationTest.java | 5 +- .../common/SchedulerJobHelper.java | 182 ++++++++---------- 3 files changed, 85 insertions(+), 115 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/api/SchedulerJobApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/api/SchedulerJobApiResource.java index f5522e18778..3d6e3daa393 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/api/SchedulerJobApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/api/SchedulerJobApiResource.java @@ -180,8 +180,9 @@ public Response executeJobByShortName( @Consumes({ MediaType.APPLICATION_JSON }) @Operation(summary = "Update a Job", description = "Updates the details of a job.") @RequestBody(required = true, content = @Content(schema = @Schema(implementation = SchedulerJobApiResourceSwagger.PutJobsJobIDRequest.class))) - @ApiResponse(responseCode = "200", description = "OK") - public String updateJobDetail(@PathParam(SchedulerJobApiConstants.JOB_ID) @Parameter(description = "jobId") final Long jobId, + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = CommandProcessingResult.class))) + public CommandProcessingResult updateJobDetail( + @PathParam(SchedulerJobApiConstants.JOB_ID) @Parameter(description = "jobId") final Long jobId, @Parameter(hidden = true) final String jsonRequestBody) { return updateJobDetail(IdTypeResolver.resolveDefault(), Objects.toString(jobId, null), jsonRequestBody); } @@ -191,8 +192,8 @@ public String updateJobDetail(@PathParam(SchedulerJobApiConstants.JOB_ID) @Param @Consumes({ MediaType.APPLICATION_JSON }) @Operation(summary = "Update a Job", description = "Updates the details of a job.") @RequestBody(required = true, content = @Content(schema = @Schema(implementation = SchedulerJobApiResourceSwagger.PutJobsJobIDRequest.class))) - @ApiResponse(responseCode = "200", description = "OK") - public String updateJobDetailByShortName( + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = CommandProcessingResult.class))) + public CommandProcessingResult updateJobDetailByShortName( @PathParam("shortName") @Parameter(required = true, description = SchedulerJobApiConstants.SHORT_NAME_PARAM) final String shortName, @Parameter(hidden = true) final String jsonRequestBody) { return updateJobDetail(IdTypeResolver.resolve(SHORT_NAME_PARAM), shortName, jsonRequestBody); @@ -250,7 +251,7 @@ private Response executeJob(@NotNull IdTypeResolver.IdType idType, String identi return response; } - private String updateJobDetail(@NotNull IdTypeResolver.IdType idType, String identifier, String jsonRequestBody) { + private CommandProcessingResult updateJobDetail(@NotNull IdTypeResolver.IdType idType, String identifier, String jsonRequestBody) { Long jobId = schedulerJobRunnerReadService.retrieveId(idType, identifier); final CommandWrapper commandRequest = new CommandWrapperBuilder() // .updateJobDetail(jobId) // @@ -261,6 +262,6 @@ private String updateJobDetail(@NotNull IdTypeResolver.IdType idType, String ide || result.getChanges().containsKey(SchedulerJobApiConstants.cronExpressionParamName))) { this.jobRegisterService.rescheduleJob(jobId); } - return this.toApiJsonSerializer.serialize(result); + return result; } } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCOBCatchUpInstanceModeIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCOBCatchUpInstanceModeIntegrationTest.java index 009b3b7f756..b4fcb896d51 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCOBCatchUpInstanceModeIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCOBCatchUpInstanceModeIntegrationTest.java @@ -117,9 +117,10 @@ public void testSchedulerWorksWhenInBatchManagerMode() { @ConfigureInstanceMode(readEnabled = true, writeEnabled = true, batchWorkerEnabled = true, batchManagerEnabled = false) @Test public void testSchedulerDoesNotWorksWhenNotInBatchManagerMode() { - CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class, + org.apache.fineract.client.feign.util.CallFailedRuntimeException exception = assertThrows( + org.apache.fineract.client.feign.util.CallFailedRuntimeException.class, () -> schedulerJobHelper.updateSchedulerStatus(false)); - assertEquals(405, exception.getResponse().code()); + assertEquals(405, exception.getStatus()); } @AfterEach diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/SchedulerJobHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/SchedulerJobHelper.java index 822a363e1ca..75ade612ff7 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/SchedulerJobHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/SchedulerJobHelper.java @@ -18,175 +18,132 @@ */ package org.apache.fineract.integrationtests.common; -import static org.apache.fineract.infrastructure.jobs.api.SchedulerJobApiConstants.SHORT_NAME_PARAM; +import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid; +import static org.apache.fineract.client.feign.util.FeignCalls.ok; import static org.awaitility.Awaitility.await; import static org.hamcrest.core.Is.is; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; import com.google.gson.Gson; -import io.restassured.builder.ResponseSpecBuilder; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializer; import io.restassured.specification.RequestSpecification; -import io.restassured.specification.ResponseSpecification; import java.time.Duration; -import java.util.HashMap; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; +import org.apache.fineract.client.models.CommandProcessingResult; +import org.apache.fineract.client.models.ExecuteJobRequest; import org.apache.fineract.client.models.GetJobsResponse; +import org.apache.fineract.client.models.GetSchedulerResponse; import org.apache.fineract.client.models.PutJobsJobIDRequest; -import org.apache.fineract.client.util.Calls; import org.hamcrest.MatcherAssert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Feign-based. Still instance-shaped (see constructor) purely for source compatibility with existing call sites; a + * follow-up PR will static-ify this class and update all callers in one mechanical pass. + */ public class SchedulerJobHelper { private static final Logger LOG = LoggerFactory.getLogger(SchedulerJobHelper.class); - private final RequestSpecification requestSpec; - private final ResponseSpecification response200Spec; + private static final Gson GSON = new GsonBuilder() + .registerTypeAdapter(OffsetDateTime.class, + (JsonSerializer) (src, type, ctx) -> src == null ? null + : new JsonPrimitive(DateTimeFormatter.ISO_INSTANT.format(src.toInstant()))) + .registerTypeAdapter(LocalDateTime.class, + (JsonSerializer) (src, type, ctx) -> src == null ? null + : new JsonPrimitive(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss").format(src))) + .registerTypeAdapter(LocalDate.class, + (JsonSerializer) (src, type, ctx) -> src == null ? null : new JsonPrimitive(src.toString())) + .create(); - // TODO: Rewrite to use fineract-client instead! - // Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long, - // org.apache.fineract.client.models.PostLoansLoanIdRequest) - @Deprecated(forRemoval = true) + /** + * @deprecated the {@code requestSpec} parameter is unused now that this Helper is Feign-based. Retained only so + * existing {@code new SchedulerJobHelper(requestSpec)} call sites keep compiling; prefer relying on + * static usage once callers are migrated in a follow-up PR. + */ + @Deprecated public SchedulerJobHelper(final RequestSpecification requestSpec) { - this.requestSpec = requestSpec; - this.response200Spec = new ResponseSpecBuilder().expectStatusCode(200).build(); + // no-op: retained for source compatibility with existing callers } - // TODO: Rewrite to use fineract-client instead! - // Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long, - // org.apache.fineract.client.models.PostLoansLoanIdRequest) - @Deprecated(forRemoval = true) - private List> getAllSchedulerJobs() { - final String GET_ALL_SCHEDULER_JOBS_URL = "/fineract-provider/api/v1/jobs?" + Utils.TENANT_IDENTIFIER; + private List getAllSchedulerJobs() { LOG.info("------------------------ RETRIEVING ALL SCHEDULER JOBS -------------------------"); - List> response = Utils.performServerGet(requestSpec, response200Spec, GET_ALL_SCHEDULER_JOBS_URL, ""); + List response = ok( + () -> FineractFeignClientHelper.getFineractFeignClient().schedulerJob().retrieveAllSchedulerJobs()); assertNotNull(response); return response; } - private List getAllSchedulerJobDetails(Function, T> mapper) { + private List getAllSchedulerJobDetails(Function mapper) { return getAllSchedulerJobs().stream().map(mapper).collect(Collectors.toList()); } public List getAllSchedulerJobIds() { - return getAllSchedulerJobDetails(map -> (Integer) map.get("jobId")); + return getAllSchedulerJobDetails(job -> job.getJobId().intValue()); } public List getAllSchedulerJobNames() { - return getAllSchedulerJobDetails(map -> (String) map.get("displayName")); + return getAllSchedulerJobDetails(GetJobsResponse::getDisplayName); } - // TODO: Rewrite to use fineract-client instead! - // Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long, - // org.apache.fineract.client.models.PostLoansLoanIdRequest) - @Deprecated(forRemoval = true) public Map getSchedulerJobById(int jobId) { - final String GET_SCHEDULER_JOB_BY_ID_URL = "/fineract-provider/api/v1/jobs/" + jobId + "?" + Utils.TENANT_IDENTIFIER; LOG.info("------------------------ RETRIEVING SCHEDULER JOB BY ID -------------------------"); - final Map response = Utils.performServerGet(requestSpec, response200Spec, GET_SCHEDULER_JOB_BY_ID_URL, ""); - LOG.info("{}", response.toString()); + GetJobsResponse response = ok( + () -> FineractFeignClientHelper.getFineractFeignClient().schedulerJob().retrieveOneSchedulerJob((long) jobId)); assertNotNull(response); - return response; + LOG.info("{}", response); + return toMap(response); } - // TODO: Rewrite to use fineract-client instead! - // Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long, - // org.apache.fineract.client.models.PostLoansLoanIdRequest) - @Deprecated(forRemoval = true) public Boolean getSchedulerStatus() { - final String GET_SCHEDULER_STATUS_URL = "/fineract-provider/api/v1/scheduler?" + Utils.TENANT_IDENTIFIER; LOG.info("------------------------ RETRIEVING SCHEDULER STATUS -------------------------"); - final Map response = Utils.performServerGet(requestSpec, response200Spec, GET_SCHEDULER_STATUS_URL, ""); - return (Boolean) response.get("active"); + GetSchedulerResponse response = ok(() -> FineractFeignClientHelper.getFineractFeignClient().scheduler().retrieveSchedulerStatus()); + return response.getActive(); } public void updateSchedulerStatus(final boolean on) { String command = on ? "start" : "stop"; - Calls.ok(FineractClientHelper.getFineractClient().jobsScheduler.handleCommandsScheduler(command)); + executeVoid(() -> FineractFeignClientHelper.getFineractFeignClient().scheduler().handleCommandsScheduler(command)); } - // TODO: Rewrite to use fineract-client instead! - // Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long, - // org.apache.fineract.client.models.PostLoansLoanIdRequest) - @Deprecated(forRemoval = true) public Map updateSchedulerJob(int jobId, final boolean active) { - final String UPDATE_SCHEDULER_JOB_URL = "/fineract-provider/api/v1/jobs/" + jobId + "?" + Utils.TENANT_IDENTIFIER; LOG.info("------------------------ UPDATING SCHEDULER JOB -------------------------"); - final Map response = Utils.performServerPut(requestSpec, response200Spec, UPDATE_SCHEDULER_JOB_URL, - updateSchedulerJobAsJSON(active), "changes"); - return response; + CommandProcessingResult response = ok(() -> FineractFeignClientHelper.getFineractFeignClient().schedulerJob() + .updateJobDetail((long) jobId, new PutJobsJobIDRequest().active(active))); + return response.getChanges(); } public void updateSchedulerJob(long jobId, PutJobsJobIDRequest request) { - Calls.ok(FineractClientHelper.getFineractClient().jobs.updateJobDetail(jobId, request)); - } - - // TODO: Rewrite to use fineract-client instead! - // Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long, - // org.apache.fineract.client.models.PostLoansLoanIdRequest) - @Deprecated(forRemoval = true) - private static String updateSchedulerJobAsJSON(final boolean active) { - final Map map = new HashMap<>(); - map.put("active", Boolean.toString(active)); - LOG.info("map : {}", map); - return new Gson().toJson(map); + ok(() -> FineractFeignClientHelper.getFineractFeignClient().schedulerJob().updateJobDetail(jobId, request)); } public void runSchedulerJob(int jobId) { - Calls.ok(FineractClientHelper.getFineractClient().jobs.executeJob((long) jobId, "executeJob")); + executeVoid(() -> FineractFeignClientHelper.getFineractFeignClient().schedulerJob().executeJob((long) jobId, "executeJob", + new ExecuteJobRequest())); } - // TODO: Rewrite to use fineract-client instead! - // Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long, - // org.apache.fineract.client.models.PostLoansLoanIdRequest) - @Deprecated(forRemoval = true) - public void runSchedulerJob(int jobId, ResponseSpecification responseSpec) { - final String RUN_SCHEDULER_JOB_URL = "/fineract-provider/api/v1/jobs/" + jobId + "?command=executeJob&" + Utils.TENANT_IDENTIFIER; - LOG.info("------------------------ RUN SCHEDULER JOB -------------------------"); - Utils.performServerPost(requestSpec, responseSpec, RUN_SCHEDULER_JOB_URL, runSchedulerJobAsJSON(), null); - } - - // TODO: Rewrite to use fineract-client instead! - // Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long, - // org.apache.fineract.client.models.PostLoansLoanIdRequest) - @Deprecated(forRemoval = true) public void runSchedulerJobByShortName(String shortName) { - final ResponseSpecification responseSpec = new ResponseSpecBuilder().expectStatusCode(202).build(); - runSchedulerJobByShortName(shortName, responseSpec); - } - - // TODO: Rewrite to use fineract-client instead! - // Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long, - // org.apache.fineract.client.models.PostLoansLoanIdRequest) - @Deprecated(forRemoval = true) - public void runSchedulerJobByShortName(String shortName, ResponseSpecification responseSpec) { - final String RUN_SCHEDULER_JOB_URL = "/fineract-provider/api/v1/jobs/" + SHORT_NAME_PARAM + "/" + shortName + "?command=executeJob&" - + Utils.TENANT_IDENTIFIER; LOG.info("------------------------ RUN SCHEDULER JOB -------------------------"); - Utils.performServerPost(requestSpec, responseSpec, RUN_SCHEDULER_JOB_URL, runSchedulerJobAsJSON(), null); - } - - // TODO: Rewrite to use fineract-client instead! - // Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long, - // org.apache.fineract.client.models.PostLoansLoanIdRequest) - @Deprecated(forRemoval = true) - private static String runSchedulerJobAsJSON() { - final Map map = new HashMap<>(); - String runSchedulerJob = new Gson().toJson(map); - LOG.info(runSchedulerJob); - return runSchedulerJob; + executeVoid(() -> FineractFeignClientHelper.getFineractFeignClient().schedulerJob().executeJobByShortName(shortName, "executeJob", + new ExecuteJobRequest())); } public int getSchedulerJobIdByName(String jobName) { - List> allSchedulerJobsData = getAllSchedulerJobs(); - for (Integer jobIndex = 0; jobIndex < allSchedulerJobsData.size(); jobIndex++) { - if (allSchedulerJobsData.get(jobIndex).get("displayName").equals(jobName)) { - return (Integer) allSchedulerJobsData.get(jobIndex).get("jobId"); + List allSchedulerJobsData = getAllSchedulerJobs(); + for (GetJobsResponse job : allSchedulerJobsData) { + if (jobName.equals(job.getDisplayName())) { + return job.getJobId().intValue(); } } throw new IllegalArgumentException( @@ -195,7 +152,7 @@ public int getSchedulerJobIdByName(String jobName) { public Long getSchedulerJobIdByShortName(String shortName) { LOG.info("------------------------ RETRIEVING SCHEDULER JOB ID BY SHORT NAME -------------------------"); - GetJobsResponse job = Calls.ok(FineractClientHelper.getFineractClient().jobs.retrieveByShortName(shortName)); + GetJobsResponse job = ok(() -> FineractFeignClientHelper.getFineractFeignClient().schedulerJob().retrieveByShortName(shortName)); assertNotNull(job); return job.getJobId(); } @@ -265,7 +222,7 @@ private void awaitJob(long jobId, Long previousRunHistoryId) { // (if any) String status = (String) finalRunHistory.get("status"); if (!status.equals("success")) { - fail("Job status is not success: " + finalRunHistory.toString()); + fail("Job status is not success for jobId=" + jobId + ": " + finalRunHistory.toString()); } } @@ -285,14 +242,25 @@ private Long getRunHistoryId(Map runHistory) { @SuppressWarnings("unchecked") private Map getLatestJobRunHistory(long jobId) { - final String GET_LATEST_SCHEDULER_JOB_RUN_HISTORY_URL = "/fineract-provider/api/v1/jobs/" + jobId - + "/runhistory?offset=0&limit=1&orderBy=id&sortOrder=DESC&" + Utils.TENANT_IDENTIFIER; LOG.info("------------------------ RETRIEVING LATEST SCHEDULER JOB RUN HISTORY -------------------------"); - Map response = Utils.performServerGet(requestSpec, response200Spec, GET_LATEST_SCHEDULER_JOB_RUN_HISTORY_URL, ""); - List> pageItems = (List>) response.get("pageItems"); + Object response = ok( + () -> FineractFeignClientHelper.getFineractFeignClient().schedulerJob().retrieveHistory(jobId, 0, 1, "id", "DESC")); + Map responseMap = toMap(response); + List> pageItems = (List>) responseMap.get("pageItems"); if (pageItems == null || pageItems.isEmpty()) { return null; } return pageItems.get(0); } + + /** + * Converts a typed Feign response into a plain {@code Map} via a JSON round-trip, preserving the + * exact field-name contract that callers previously relied on when this Helper deserialized raw RestAssured JSON + * responses into generic maps. Uses a Gson instance with explicit {@code java.time} serializers (rather than a bare + * {@code new Gson()}) so date/time fields round-trip as plain ISO-8601 strings, matching what the original raw + * server JSON produced, instead of Gson's default reflective (and much uglier) field-by-field breakdown. + */ + private static Map toMap(Object typedResponse) { + return GSON.fromJson(GSON.toJson(typedResponse), Map.class); + } }