Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@
import static org.apache.fineract.commands.domain.CommandWrapperConstants.ACTION_DOWNPAYMENT;
import static org.apache.fineract.commands.domain.CommandWrapperConstants.ACTION_ENABLE;
import static org.apache.fineract.commands.domain.CommandWrapperConstants.ACTION_EXECUTE;
import static org.apache.fineract.commands.domain.CommandWrapperConstants.ACTION_EXECUTEJOB;
import static org.apache.fineract.commands.domain.CommandWrapperConstants.ACTION_FORCE_WITHDRAWAL;
import static org.apache.fineract.commands.domain.CommandWrapperConstants.ACTION_FORECLOSURE;
import static org.apache.fineract.commands.domain.CommandWrapperConstants.ACTION_GET;
Expand Down Expand Up @@ -2565,14 +2564,6 @@ public CommandWrapperBuilder updateJobDetail(final Long jobId) {
return this;
}

public CommandWrapperBuilder executeSchedulerJob(final Long jobId) {
this.actionName = ACTION_EXECUTEJOB;
this.entityName = ENTITY_SCHEDULER;
this.entityId = jobId;
this.href = "/jobs/" + jobId + "?command=executeJob";
return this;
}

/**
* Deposit account mappings
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class JobParameterDTO {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,21 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.ExecuteJobRequest;
import org.apache.fineract.client.models.GetJobsJobIDJobRunHistoryResponse;
import org.apache.fineract.client.models.JobDetailHistoryDataSwagger;
import org.apache.fineract.client.models.JobExecuteRequest;
import org.apache.fineract.test.data.job.Job;
import org.apache.fineract.test.data.job.JobResolver;
import org.apache.fineract.test.messaging.config.JobPollingProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
@Slf4j
public class JobService {

@Autowired
private FineractFeignClient fineractClient;

@Autowired
private JobPollingProperties jobPollingProperties;

private final FineractFeignClient fineractClient;
private final JobPollingProperties jobPollingProperties;
private final JobResolver jobResolver;

public void execute(Job job) {
Expand All @@ -59,7 +54,7 @@ public void execute(Job job) {
private void execute(Long jobId) {
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("command", "executeJob");
executeVoid(() -> fineractClient.schedulerJob().executeJob(jobId, new ExecuteJobRequest(), queryParams));
executeVoid(() -> fineractClient.schedulerJob().executeJob(jobId, new JobExecuteRequest(), queryParams));
}

public void executeAndWait(Job job) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.ExecuteJobRequest;
import org.apache.fineract.client.models.GetJobsResponse;
import org.apache.fineract.client.models.JobExecuteRequest;
import org.apache.fineract.client.models.PutJobsJobIDRequest;
import org.springframework.stereotype.Component;

Expand Down Expand Up @@ -65,7 +65,7 @@ private void enableAndExecuteEventJob() throws InterruptedException {

// Manually execute once immediately to publish any queued events from initialization
log.debug("Manually executing '{}' job once to publish queued events...", SEND_ASYNCHRONOUS_EVENTS_JOB_NAME);
executeVoid(() -> fineractClient.schedulerJob().executeJob(jobId, new ExecuteJobRequest(), Map.of("command", "executeJob")));
executeVoid(() -> fineractClient.schedulerJob().executeJob(jobId, new JobExecuteRequest(), Map.of("command", "executeJob")));

// Poll job history to confirm it ran
log.debug("Polling job history to confirm initial execution...");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,13 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "UPDATE_HOOK")
.requestMatchers(API_MATCHER.matcher(HttpMethod.DELETE, "/api/*/hooks/*"))
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "DELETE_HOOK")

// execute scheduler job (by id)
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/jobs/*"))
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "EXECUTEJOB_SCHEDULER")
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/jobs/short-name/*"))
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "EXECUTEJOB_SCHEDULER")

// template
.requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/templates/*"))
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_READ, "READ_TEMPLATE")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.command.core.CommandDispatcher;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
Expand All @@ -62,11 +63,13 @@
import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.SearchParameters;
import org.apache.fineract.infrastructure.jobs.command.JobExecuteCommand;
import org.apache.fineract.infrastructure.jobs.data.JobDetailData;
import org.apache.fineract.infrastructure.jobs.data.JobDetailHistoryData;
import org.apache.fineract.infrastructure.jobs.data.JobExecuteRequest;
import org.apache.fineract.infrastructure.jobs.data.JobExecuteResponse;
import org.apache.fineract.infrastructure.jobs.service.JobRegisterService;
import org.apache.fineract.infrastructure.jobs.service.SchedulerJobRunnerReadService;
import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.infrastructure.security.service.SqlValidator;
import org.springframework.stereotype.Component;
Expand All @@ -87,6 +90,7 @@ public class SchedulerJobApiResource {
private final PlatformSecurityContext context;
private final FineractProperties fineractProperties;
private final SqlValidator sqlValidator;
private final CommandDispatcher dispatcher;

@GET
@Operation(summary = "Retrieve Scheduler Jobs", operationId = "retrieveAllSchedulerJobs", description = "Returns the list of jobs.\n"
Expand Down Expand Up @@ -151,25 +155,36 @@ public String retrieveHistoryByShortName(@Context final UriInfo uriInfo,
@Path("{" + SchedulerJobApiConstants.JOB_ID + "}")
@Consumes({ MediaType.APPLICATION_JSON })
@Operation(summary = "Run a Job", description = "Manually Execute Specific Job.")
@RequestBody(content = @Content(schema = @Schema(implementation = SchedulerJobApiResourceSwagger.ExecuteJobRequest.class)))
@ApiResponse(responseCode = "200", description = "POST: jobs/1?command=executeJob")
public Response executeJob(@PathParam(SchedulerJobApiConstants.JOB_ID) @Parameter(description = "jobId") final Long jobId,
@QueryParam(SchedulerJobApiConstants.COMMAND) @Parameter(description = "command") final String commandParam,
@Parameter(hidden = true) final String jsonRequestBody) {
return executeJob(IdTypeResolver.resolveDefault(), Objects.toString(jobId, null), commandParam, jsonRequestBody);
public Response executeJob(@PathParam(SchedulerJobApiConstants.JOB_ID) final Long jobId,
@QueryParam(SchedulerJobApiConstants.COMMAND) final String commandParam, JobExecuteRequest request) {
return dispatchExecuteJob(jobId, commandParam, request);
}

@POST
@Path(SHORT_NAME_PARAM + "/{shortName}")
@Consumes({ MediaType.APPLICATION_JSON })
@Operation(summary = "Run a Job", description = "Manually Execute Specific Job.")
@RequestBody(content = @Content(schema = @Schema(implementation = SchedulerJobApiResourceSwagger.ExecuteJobRequest.class)))
@ApiResponse(responseCode = "200", description = "POST: jobs/short-name/SA_PINT?command=executeJob")
public Response executeJobByShortName(
@PathParam("shortName") @Parameter(required = true, description = SchedulerJobApiConstants.SHORT_NAME_PARAM) final String shortName,
@QueryParam(SchedulerJobApiConstants.COMMAND) @Parameter(description = "command") final String commandParam,
@Parameter(hidden = true) final String jsonRequestBody) {
return executeJob(IdTypeResolver.resolve(SHORT_NAME_PARAM), shortName, commandParam, jsonRequestBody);
@Operation(summary = "Run a Job", description = "Manually Execute Specific Job (by short name).")
public Response executeJobByShortName(@PathParam("shortName") final String shortName,
@QueryParam(SchedulerJobApiConstants.COMMAND) final String commandParam, JobExecuteRequest request) {
final Long jobId = schedulerJobRunnerReadService.retrieveId(IdTypeResolver.IdType.SHORT_NAME, shortName);
return dispatchExecuteJob(jobId, commandParam, request);
}

private Response dispatchExecuteJob(Long jobId, String commandParam, JobExecuteRequest request) {
if (!fineractProperties.getMode().isBatchManagerEnabled()) {
return Response.status(Status.METHOD_NOT_ALLOWED).entity(ApiGlobalErrorResponse.invalidInstanceTypeMethod("Batch")).build();
}
if (!is(commandParam, SchedulerJobApiConstants.COMMAND_EXECUTE_JOB)) {
throw new UnrecognizedQueryParamException(SchedulerJobApiConstants.COMMAND, commandParam);
}
if (request == null) {
request = JobExecuteRequest.builder().build();
}
request.setJobId(jobId);
var command = new JobExecuteCommand();
command.setPayload(request);
dispatcher.<JobExecuteRequest, JobExecuteResponse>dispatch(command).get();
return Response.status(202).build();
}

@PUT
Expand Down Expand Up @@ -219,34 +234,6 @@ private String retrieveHistory(@NotNull IdTypeResolver.IdType idType, String ide
return jobHistoryToApiJsonSerializer.serialize(settings, jobHistoryData, JOB_HISTORY_RESPONSE_DATA_PARAMETERS);
}

private Response executeJob(@NotNull IdTypeResolver.IdType idType, String identifier, String commandParam, String jsonRequestBody) {
// check the logged-in user have permissions to execute scheduler jobs
Response response;
if (fineractProperties.getMode().isBatchManagerEnabled()) {
final boolean hasNotPermission = context.authenticatedUser().hasNotPermissionForAnyOf("ALL_FUNCTIONS", "EXECUTEJOB_SCHEDULER");
if (hasNotPermission) {
final String authorizationMessage = "User has no authority to execute scheduler jobs";
throw new NoAuthorizationException(authorizationMessage);
}
response = Response.status(400).build();
if (is(commandParam, SchedulerJobApiConstants.COMMAND_EXECUTE_JOB)) {
Long jobId = schedulerJobRunnerReadService.retrieveId(idType, identifier);
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
.executeSchedulerJob(jobId) //
.withJson(jsonRequestBody) //
.build();
commandsSourceWritePlatformService.logCommandSource(commandRequest);
response = Response.status(202).build();
} else {
throw new UnrecognizedQueryParamException(SchedulerJobApiConstants.COMMAND, commandParam);
}
} else {
ApiGlobalErrorResponse errorResponse = ApiGlobalErrorResponse.invalidInstanceTypeMethod("Batch");
response = Response.status(Status.METHOD_NOT_ALLOWED).entity(errorResponse).build();
}
return response;
}

private String updateJobDetail(@NotNull IdTypeResolver.IdType idType, String identifier, String jsonRequestBody) {
Long jobId = schedulerJobRunnerReadService.retrieveId(idType, identifier);
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,10 @@ private JobDetailHistoryDataSwagger() {}

}

@Schema(description = "ExecuteJobRequest")
public static final class ExecuteJobRequest {
@Schema(description = "JobExecuteRequest")
public static final class JobExecuteRequest {

private ExecuteJobRequest() {
private JobExecuteRequest() {

}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.fineract.infrastructure.jobs.command;

import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.fineract.command.core.Command;
import org.apache.fineract.infrastructure.jobs.data.JobExecuteRequest;

@Data
@EqualsAndHashCode(callSuper = true)
public class JobExecuteCommand extends Command<JobExecuteRequest> {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.fineract.infrastructure.jobs.data;

import java.io.Serial;
import java.io.Serializable;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldNameConstants;

@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@FieldNameConstants
public class JobExecuteRequest implements Serializable {

@Serial
private static final long serialVersionUID = 1L;

private Long jobId;

private List<JobParameterDTO> parameters;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.fineract.infrastructure.jobs.data;

import java.io.Serial;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class JobExecuteResponse implements Serializable {

@Serial
private static final long serialVersionUID = 1L;

private Long resourceId;
}

This file was deleted.

Loading
Loading