feat: add command handlers, permissions, and API resources for surveys and loan COB catch-up
Fineract Build & Cucumber tests (without E2E tests) / verify (main, build-core) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, cucumber) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (progressive-loan, build-progressive-loan) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 8 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 9 of 10) (push) Canceled after 0s
Fineract Docker Builds / build (false, docker-compose-postgresql.yml, postgresql) (push) Canceled after 0s
Fineract Docker Builds / build (true, docker-compose.yml, mariadb) (push) Canceled after 0s
Fineract Documentation build / build (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 1 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 10 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 2 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 3 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 4 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 5 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 6 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 7 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-5) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-5) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-5) (push) Canceled after 0s
Fineract Liquibase Only mode - PostgreSQL / test (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with ActiveMQ (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with Kafka (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, build-core) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, cucumber) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (progressive-loan, build-progressive-loan) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 8 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 9 of 10) (push) Canceled after 0s
Fineract Docker Builds / build (false, docker-compose-postgresql.yml, postgresql) (push) Canceled after 0s
Fineract Docker Builds / build (true, docker-compose.yml, mariadb) (push) Canceled after 0s
Fineract Documentation build / build (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 1 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 10 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 2 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 3 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 4 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 5 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 6 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 7 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-5) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-5) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-5) (push) Canceled after 0s
Fineract Liquibase Only mode - PostgreSQL / test (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with ActiveMQ (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with Kafka (push) Canceled after 0s
This commit is contained in:
+32
@@ -4809,4 +4809,36 @@ public class CommandWrapperBuilder {
|
||||
this.href = "/loans/" + loanId + "/originators/" + originatorId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createSurvey() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "SURVEY";
|
||||
this.entityId = null;
|
||||
this.href = "/surveys";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder updateSurvey(final Long surveyId) {
|
||||
this.actionName = "UPDATE";
|
||||
this.entityName = "SURVEY";
|
||||
this.entityId = surveyId;
|
||||
this.href = "/surveys/" + surveyId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder activateSurvey(final Long surveyId) {
|
||||
this.actionName = "ACTIVATE";
|
||||
this.entityName = "SURVEY";
|
||||
this.entityId = surveyId;
|
||||
this.href = "/surveys/" + surveyId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder deactivateSurvey(final Long surveyId) {
|
||||
this.actionName = "DEACTIVATE";
|
||||
this.entityName = "SURVEY";
|
||||
this.entityId = surveyId;
|
||||
this.href = "/surveys/" + surveyId;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -35,6 +35,7 @@ import org.apache.fineract.cob.data.OldestCOBProcessedLoanDTO;
|
||||
import org.apache.fineract.cob.service.LoanCOBCatchUpService;
|
||||
import org.apache.fineract.infrastructure.core.exception.JobIsNotFoundOrNotEnabledException;
|
||||
import org.apache.fineract.infrastructure.jobs.service.JobName;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/loans")
|
||||
@@ -44,6 +45,7 @@ import org.springframework.stereotype.Component;
|
||||
public class LoanCOBCatchUpApiResource {
|
||||
|
||||
private final Optional<LoanCOBCatchUpService> loanCOBCatchUpServiceOp;
|
||||
private final PlatformSecurityContext context;
|
||||
|
||||
@GET
|
||||
@Path("oldest-cob-closed")
|
||||
@@ -64,6 +66,7 @@ public class LoanCOBCatchUpApiResource {
|
||||
@ApiResponse(responseCode = "202", description = "Catch Up has been started")
|
||||
@ApiResponse(responseCode = "400", description = "Catch Up is already running")
|
||||
public Response executeLoanCOBCatchUp() {
|
||||
this.context.authenticatedUser().validateHasPermissionTo("EXECUTE_LOANCOBCATCHUP");
|
||||
return loanCOBCatchUpServiceOp.map(loanCOBCatchUpService -> {
|
||||
if (loanCOBCatchUpService.isCatchUpRunning().isCatchUpRunning()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST).build();
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
*/
|
||||
package org.apache.fineract.spm.api;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
||||
@@ -37,10 +36,14 @@ import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.spm.data.SurveyData;
|
||||
import org.apache.fineract.spm.domain.Survey;
|
||||
@@ -57,6 +60,8 @@ public class SpmApiResource {
|
||||
|
||||
private final PlatformSecurityContext securityContext;
|
||||
private final SpmService spmService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final DefaultToApiJsonSerializer<CommandProcessingResult> toApiJsonSerializer;
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -103,12 +108,10 @@ public class SpmApiResource {
|
||||
@Operation(summary = "Create a Survey", description = "Adds a new survey to collect client related data.\n" + "\n"
|
||||
+ "Mandatory Fields\n" + "\n" + "countryCode, key, name, questions, responses, sequenceNo, text, description")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public String createSurvey(@Parameter(description = "Create survey") final SurveyData surveyData) {
|
||||
this.securityContext.authenticatedUser();
|
||||
final Survey survey = SurveyMapper.map(surveyData, new Survey());
|
||||
this.spmService.createSurvey(survey);
|
||||
return getResponse(survey.getId());
|
||||
|
||||
public String createSurvey(@Parameter(description = "Create survey") final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createSurvey().withJson(apiRequestBodyAsJson).build();
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@@ -116,12 +119,10 @@ public class SpmApiResource {
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Transactional
|
||||
public String editSurvey(@PathParam("id") final Long id, final SurveyData surveyData) {
|
||||
this.securityContext.authenticatedUser();
|
||||
final Survey surveyToUpdate = this.spmService.findById(id);
|
||||
final Survey survey = SurveyMapper.map(surveyData, surveyToUpdate);
|
||||
this.spmService.updateSurvey(survey);
|
||||
return getResponse(survey.getId());
|
||||
public String editSurvey(@PathParam("id") final Long id, final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateSurvey(id).withJson(apiRequestBodyAsJson).build();
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@@ -131,22 +132,17 @@ public class SpmApiResource {
|
||||
@Transactional
|
||||
@Operation(summary = "Deactivate Survey", description = "")
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
|
||||
public void activateOrDeactivateSurvey(@PathParam("id") final Long id, @QueryParam("command") final String command) {
|
||||
this.securityContext.authenticatedUser();
|
||||
public String activateOrDeactivateSurvey(@PathParam("id") final Long id, @QueryParam("command") final String command) {
|
||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder();
|
||||
final CommandWrapper commandRequest;
|
||||
if (command != null && command.equalsIgnoreCase("activate")) {
|
||||
this.spmService.activateSurvey(id);
|
||||
commandRequest = builder.activateSurvey(id).build();
|
||||
} else if (command != null && command.equalsIgnoreCase("deactivate")) {
|
||||
this.spmService.deactivateSurvey(id);
|
||||
commandRequest = builder.deactivateSurvey(id).build();
|
||||
} else {
|
||||
throw new UnrecognizedQueryParamException("command", command);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String getResponse(Long id) {
|
||||
Gson gson = new Gson();
|
||||
HashMap<String, Object> response = new HashMap<>();
|
||||
response.put("resourceId", id);
|
||||
return gson.toJson(response);
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
}
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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.spm.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.spm.service.SpmService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "SURVEY", action = "ACTIVATE")
|
||||
public class ActivateSurveyCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SpmService spmService;
|
||||
|
||||
@Autowired
|
||||
public ActivateSurveyCommandHandler(final SpmService spmService) {
|
||||
this.spmService = spmService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
|
||||
this.spmService.activateSurvey(command.entityId());
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(command.entityId()) //
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* 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.spm.handler;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.spm.data.SurveyData;
|
||||
import org.apache.fineract.spm.domain.Survey;
|
||||
import org.apache.fineract.spm.service.SpmService;
|
||||
import org.apache.fineract.spm.util.SurveyMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "SURVEY", action = "CREATE")
|
||||
public class CreateSurveyCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SpmService spmService;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
public CreateSurveyCommandHandler(final SpmService spmService, final ObjectMapper objectMapper) {
|
||||
this.spmService = spmService;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
|
||||
final SurveyData surveyData = readSurveyData(command);
|
||||
final Survey survey = SurveyMapper.map(surveyData, new Survey());
|
||||
this.spmService.createSurvey(survey);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(survey.getId()) //
|
||||
.build();
|
||||
}
|
||||
|
||||
private SurveyData readSurveyData(final JsonCommand command) {
|
||||
try {
|
||||
return this.objectMapper.readValue(command.json(), SurveyData.class);
|
||||
} catch (final JsonProcessingException e) {
|
||||
throw new PlatformDataIntegrityException("error.msg.survey.invalid.json", "Invalid survey payload: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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.spm.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.spm.service.SpmService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "SURVEY", action = "DEACTIVATE")
|
||||
public class DeactivateSurveyCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SpmService spmService;
|
||||
|
||||
@Autowired
|
||||
public DeactivateSurveyCommandHandler(final SpmService spmService) {
|
||||
this.spmService = spmService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
|
||||
this.spmService.deactivateSurvey(command.entityId());
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(command.entityId()) //
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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.spm.handler;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.spm.data.SurveyData;
|
||||
import org.apache.fineract.spm.domain.Survey;
|
||||
import org.apache.fineract.spm.service.SpmService;
|
||||
import org.apache.fineract.spm.util.SurveyMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "SURVEY", action = "UPDATE")
|
||||
public class UpdateSurveyCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SpmService spmService;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
public UpdateSurveyCommandHandler(final SpmService spmService, final ObjectMapper objectMapper) {
|
||||
this.spmService = spmService;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
|
||||
final SurveyData surveyData = readSurveyData(command);
|
||||
final Survey surveyToUpdate = this.spmService.findById(command.entityId());
|
||||
final Survey survey = SurveyMapper.map(surveyData, surveyToUpdate);
|
||||
this.spmService.updateSurvey(survey);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(survey.getId()) //
|
||||
.build();
|
||||
}
|
||||
|
||||
private SurveyData readSurveyData(final JsonCommand command) {
|
||||
try {
|
||||
return this.objectMapper.readValue(command.json(), SurveyData.class);
|
||||
} catch (final JsonProcessingException e) {
|
||||
throw new PlatformDataIntegrityException("error.msg.survey.invalid.json", "Invalid survey payload: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
-1
@@ -114,17 +114,33 @@ public class PermissionReadPlatformServiceImpl implements PermissionReadPlatform
|
||||
+ "'SUBMITEDIT_CURRENTACCOUNT','APPROVEEDIT_CURRENTACCOUNT','REJECTEDIT_CURRENTACCOUNT',"
|
||||
+ "'SUBMITEDIT_CLIENT','APPROVEEDIT_CLIENT','REJECTEDIT_CLIENT'";
|
||||
|
||||
/*
|
||||
* Unlike DEAD_MAKER_CHECKER_CODES above, these codes ARE read by the live maker-checker pipeline
|
||||
* (CommandSourceService.isMakerCheckerEnabledForTask()) - they are excluded from this screen for a
|
||||
* different reason: UPDATE_PERMISSION governs this very "Configure Maker Checker Tasks" screen
|
||||
* (toggling which tasks require maker-checker), and UPDATE_CONFIGURATION governs the global
|
||||
* maker-checker on/off switch and other System -> Configurations settings. If either were flipped
|
||||
* to can_maker_checker=true and the approving checker.user lacked CHECKER_SUPER_USER, an admin
|
||||
* could lock themselves out of ever approving further changes to maker-checker configuration
|
||||
* itself, with no ordinary way to reverse it. Excluding them here only hides them from this
|
||||
* screen; it does not touch their can_maker_checker value or their normal enforcement.
|
||||
*/
|
||||
private static final String SELF_REFERENTIAL_MAKER_CHECKER_CODES = "'UPDATE_PERMISSION','UPDATE_CONFIGURATION'";
|
||||
|
||||
public String makerCheckerablePermissionSchema() {
|
||||
/*
|
||||
* get all 'Maker-Checkerable' permissions - Maintenance permissions (i.e. exclude the 'special' grouping,
|
||||
* the READ permissions and the CHECKER permissions), and exclude permission codes whose approval
|
||||
* workflow does not actually read can_maker_checker (see DEAD_MAKER_CHECKER_CODES above).
|
||||
* workflow does not actually read can_maker_checker (see DEAD_MAKER_CHECKER_CODES above), as well as
|
||||
* codes that control maker-checker configuration itself (see SELF_REFERENTIAL_MAKER_CHECKER_CODES
|
||||
* above) to avoid a self-lockout.
|
||||
*/
|
||||
|
||||
return "select p.grouping, p.code, p.entity_name as entityName, p.action_name as actionName, p.can_maker_checker as selected"
|
||||
+ " from m_permission p " + " where " + sqlGenerator.escape("grouping")
|
||||
+ " != 'special' and code not like 'READ_%' and code not like '%\\_CHECKER'"
|
||||
+ " and code not in (" + DEAD_MAKER_CHECKER_CODES + ")"
|
||||
+ " and code not in (" + SELF_REFERENTIAL_MAKER_CHECKER_CODES + ")"
|
||||
+ " order by p.grouping, coalesce(entity_name, ''), p.code";
|
||||
}
|
||||
|
||||
|
||||
@@ -149,4 +149,7 @@
|
||||
dev-latest's highest, 0582) to avoid ambiguity; content/semantics unchanged, only the file
|
||||
name and this changeset's id were updated to match. -->
|
||||
<include file="parts/0583_add_loan_originator_checker_permissions.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0584_add_manage_survey_permissions.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0585_add_loan_cob_catchup_permission.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0586_add_system_module_checker_permissions.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<!-- The Manage Surveys screen (SPM scorecards, spm.api.SpmApiResource) previously performed
|
||||
create/update/activate/deactivate directly against SpmService with only a login check -
|
||||
no permission check at any layer. These 4 write operations have now been wired into the
|
||||
standard command pipeline (CommandWrapperBuilder.createSurvey/updateSurvey/activateSurvey/
|
||||
deactivateSurvey -> CreateSurveyCommandHandler/UpdateSurveyCommandHandler/
|
||||
ActivateSurveyCommandHandler/DeactivateSurveyCommandHandler, entity "SURVEY"), which requires
|
||||
these permission rows to exist so PortfolioCommandSourceWritePlatformServiceImpl.logCommandSource()
|
||||
can authorize the caller. Seeded with can_maker_checker=false: no prior maker-checker behaviour
|
||||
existed for this screen, and enabling maker-checker is a business decision left for later. -->
|
||||
<changeSet id="0584_add_manage_survey_permissions" author="cbs-anshul">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'CREATE_SURVEY'</sqlCheck>
|
||||
</preConditions>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="configuration"/>
|
||||
<column name="code" value="CREATE_SURVEY"/>
|
||||
<column name="entity_name" value="SURVEY"/>
|
||||
<column name="action_name" value="CREATE"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="configuration"/>
|
||||
<column name="code" value="UPDATE_SURVEY"/>
|
||||
<column name="entity_name" value="SURVEY"/>
|
||||
<column name="action_name" value="UPDATE"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="configuration"/>
|
||||
<column name="code" value="ACTIVATE_SURVEY"/>
|
||||
<column name="entity_name" value="SURVEY"/>
|
||||
<column name="action_name" value="ACTIVATE"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="configuration"/>
|
||||
<column name="code" value="DEACTIVATE_SURVEY"/>
|
||||
<column name="entity_name" value="SURVEY"/>
|
||||
<column name="action_name" value="DEACTIVATE"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
<!-- No role previously held any permission for this screen (it had no permission check at all),
|
||||
so there are no "roles that already hold the base permission" to copy from. Following the
|
||||
0511_institution_and_branch_profile.xml convention for a brand-new permission model: grant to
|
||||
Super user plus any manager-named role. -->
|
||||
<changeSet id="0584_grant_manage_survey_permissions" author="cbs-anshul">
|
||||
<sql>
|
||||
INSERT INTO m_role_permission (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM m_role r
|
||||
JOIN m_permission p ON p.code IN ('CREATE_SURVEY', 'UPDATE_SURVEY', 'ACTIVATE_SURVEY', 'DEACTIVATE_SURVEY')
|
||||
WHERE (LOWER(r.name) LIKE '%manager%' OR LOWER(r.name) = 'super user')
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM m_role_permission rp2
|
||||
WHERE rp2.role_id = r.id AND rp2.permission_id = p.id
|
||||
)
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<!-- LoanCOBCatchUpApiResource.executeLoanCOBCatchUp() (POST /v1/loans/catch-up) previously had no
|
||||
permission check at all. A validateHasPermissionTo("EXECUTE_LOANCOBCATCHUP") check was added
|
||||
directly (not full CommandWrapper/maker-checker wiring, since this is a job-execution trigger,
|
||||
not a CRUD operation on a persisted entity - matching the existing EXECUTE_INLINE_JOB precedent),
|
||||
which requires this permission row to exist. can_maker_checker=false: this closes the
|
||||
authorization gap only, it does not introduce maker-checker for job execution. -->
|
||||
<changeSet id="0585_add_loan_cob_catchup_permission" author="cbs-anshul">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'EXECUTE_LOANCOBCATCHUP'</sqlCheck>
|
||||
</preConditions>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="organisation"/>
|
||||
<column name="code" value="EXECUTE_LOANCOBCATCHUP"/>
|
||||
<column name="entity_name" value="LOANCOBCATCHUP"/>
|
||||
<column name="action_name" value="EXECUTE"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
<!-- No role previously held any permission for this endpoint (it had no permission check at all),
|
||||
so there are no "roles that already hold the base permission" to copy from. Following the
|
||||
0511_institution_and_branch_profile.xml convention for a brand-new permission model: grant to
|
||||
Super user plus any manager-named role. -->
|
||||
<changeSet id="0585_grant_loan_cob_catchup_permission" author="cbs-anshul">
|
||||
<sql>
|
||||
INSERT INTO m_role_permission (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM m_role r
|
||||
JOIN m_permission p ON p.code = 'EXECUTE_LOANCOBCATCHUP'
|
||||
WHERE (LOWER(r.name) LIKE '%manager%' OR LOWER(r.name) = 'super user')
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM m_role_permission rp2
|
||||
WHERE rp2.role_id = r.id AND rp2.permission_id = p.id
|
||||
)
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<!-- The Admin -> System maker-checker audit found 8 base permissions whose "_CHECKER" counterpart
|
||||
row was never seeded (AppUser.validateHasCheckerPermissionTo() computes
|
||||
checkerPermissionName = code.toUpperCase() + "_CHECKER" and requires the approver to hold
|
||||
either CHECKER_SUPER_USER or that exact permission code - see the identical issue already
|
||||
fixed for LOAN_ORIGINATOR in 0583_add_loan_originator_checker_permissions.xml). Without these
|
||||
rows, no ordinary role can ever be granted checker rights for these operations, and
|
||||
UPDATE_BATCH_BUSINESS_STEP (Manage Jobs - COB Workflow Step Reorder) is the one permission in
|
||||
all of Admin -> System that already has can_maker_checker=true today, making its missing
|
||||
checker row a live gap, not merely a structural one. All 8 rows are seeded with
|
||||
can_maker_checker=false: this changeset only makes the approval path structurally ready, it
|
||||
does not itself enable maker-checker on any base permission.
|
||||
|
||||
For CREATE/UPDATE/DELETE_ACCOUNTNUMBERFORMAT_CHECKER, entity_name is set to
|
||||
"ACCOUNTNUMBERFORMAT" for all three (matching CREATE_ACCOUNTNUMBERFORMAT and
|
||||
UPDATE_ACCOUNTNUMBERFORMAT). The pre-existing base permission DELETE_ACCOUNTNUMBERFORMAT has
|
||||
entity_name="HOOK" (a pre-existing data bug, confirmed via read-only query, left untouched -
|
||||
fixing an existing permission row's value was out of scope for this change). This checker row
|
||||
deliberately uses the correct entity name rather than propagating that bug. -->
|
||||
<changeSet id="0586_add_system_module_checker_permissions" author="cbs-anshul">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'CREATE_ACCOUNTNUMBERFORMAT_CHECKER'</sqlCheck>
|
||||
</preConditions>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="configuration"/>
|
||||
<column name="code" value="CREATE_ACCOUNTNUMBERFORMAT_CHECKER"/>
|
||||
<column name="entity_name" value="ACCOUNTNUMBERFORMAT"/>
|
||||
<column name="action_name" value="CREATE_CHECKER"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="configuration"/>
|
||||
<column name="code" value="UPDATE_ACCOUNTNUMBERFORMAT_CHECKER"/>
|
||||
<column name="entity_name" value="ACCOUNTNUMBERFORMAT"/>
|
||||
<column name="action_name" value="UPDATE_CHECKER"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="configuration"/>
|
||||
<column name="code" value="DELETE_ACCOUNTNUMBERFORMAT_CHECKER"/>
|
||||
<column name="entity_name" value="ACCOUNTNUMBERFORMAT"/>
|
||||
<column name="action_name" value="DELETE_CHECKER"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="infrastructure"/>
|
||||
<column name="code" value="CREATE_ENTITYMAPPING_CHECKER"/>
|
||||
<column name="entity_name" value="ENTITYMAPPING"/>
|
||||
<column name="action_name" value="CREATE_CHECKER"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="infrastructure"/>
|
||||
<column name="code" value="UPDATE_ENTITYMAPPING_CHECKER"/>
|
||||
<column name="entity_name" value="ENTITYMAPPING"/>
|
||||
<column name="action_name" value="UPDATE_CHECKER"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="infrastructure"/>
|
||||
<column name="code" value="DELETE_ENTITYMAPPING_CHECKER"/>
|
||||
<column name="entity_name" value="ENTITYMAPPING"/>
|
||||
<column name="action_name" value="DELETE_CHECKER"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="organisation"/>
|
||||
<column name="code" value="CREATE_USERAGREEMENT_CHECKER"/>
|
||||
<column name="entity_name" value="USERAGREEMENT"/>
|
||||
<column name="action_name" value="CREATE_CHECKER"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="organisation"/>
|
||||
<column name="code" value="UPDATE_BATCH_BUSINESS_STEP_CHECKER"/>
|
||||
<column name="entity_name" value="BATCH_BUSINESS_STEP"/>
|
||||
<column name="action_name" value="UPDATE_CHECKER"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
<!-- Granted to whichever roles already hold the corresponding base permission - the same
|
||||
"grant to roles that already hold the base permission" convention used in
|
||||
0583_add_loan_originator_checker_permissions.xml. -->
|
||||
<changeSet id="0586_grant_system_module_checker_permissions" author="cbs-anshul">
|
||||
<sql>
|
||||
INSERT INTO m_role_permission (role_id, permission_id)
|
||||
SELECT rp.role_id, checker.id
|
||||
FROM m_role_permission rp
|
||||
JOIN m_permission base ON base.id = rp.permission_id
|
||||
JOIN m_permission checker ON checker.code = base.code || '_CHECKER'
|
||||
WHERE base.code IN ('CREATE_ACCOUNTNUMBERFORMAT', 'UPDATE_ACCOUNTNUMBERFORMAT', 'DELETE_ACCOUNTNUMBERFORMAT',
|
||||
'CREATE_ENTITYMAPPING', 'UPDATE_ENTITYMAPPING', 'DELETE_ENTITYMAPPING',
|
||||
'CREATE_USERAGREEMENT', 'UPDATE_BATCH_BUSINESS_STEP')
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM m_role_permission rp2
|
||||
WHERE rp2.role_id = rp.role_id AND rp2.permission_id = checker.id
|
||||
)
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
Reference in New Issue
Block a user