FINERACT-2421: Rework code value handling in E2E

This commit is contained in:
Adam Saghy
2026-01-16 17:59:14 +01:00
parent 0f73ae1eb7
commit 0891adb267
18 changed files with 328 additions and 148 deletions
@@ -41,6 +41,7 @@ import jakarta.ws.rs.core.UriInfo;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.commands.domain.CommandWrapper;
@@ -49,6 +50,7 @@ import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformS
import org.apache.fineract.infrastructure.codes.CodeConstants.CodevalueJSONinputParams;
import org.apache.fineract.infrastructure.codes.data.CodeData;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.codes.service.CodeReadPlatformService;
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
@@ -57,7 +59,7 @@ import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSer
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.stereotype.Component;
@Path("/v1/codes/{codeId}/codevalues")
@Path("/v1/codes")
@Component
@Tag(name = "Code Values", description = "Code and code values: Codes represent a specific category of data, their code values are a specific instance of that category.\n"
+ "\n"
@@ -76,11 +78,13 @@ public class CodeValuesApiResource {
private final PlatformSecurityContext context;
private final CodeValueReadPlatformService readPlatformService;
private final CodeReadPlatformService codeReadPlatformService;
private final DefaultToApiJsonSerializer<CodeValueData> toApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@GET
@Path("{codeId}/codevalues")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "List Code Values", description = "Returns the list of Code Values for a given Code\n" + "\n"
@@ -99,7 +103,7 @@ public class CodeValuesApiResource {
}
@GET
@Path("{codeValueId}")
@Path("{codeId}/codevalues/{codeValueId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve a Code description", description = "Returns the details of a Code Value\n" + "\n" + "Example Requests:\n"
@@ -119,6 +123,7 @@ public class CodeValuesApiResource {
}
@POST
@Path("{codeId}/codevalues")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Create a Code description", description = "")
@@ -137,7 +142,7 @@ public class CodeValuesApiResource {
}
@PUT
@Path("{codeValueId}")
@Path("{codeId}/codevalues/{codeValueId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Update a Code description", description = "Updates the details of a code description.")
@@ -157,7 +162,7 @@ public class CodeValuesApiResource {
}
@DELETE
@Path("{codeValueId}")
@Path("{codeId}/codevalues/{codeValueId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Delete a Code description", description = "Deletes a code description")
@@ -172,4 +177,89 @@ public class CodeValuesApiResource {
return this.toApiJsonSerializer.serialize(result);
}
@GET
@Path("name/{codeName}/codevalues")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "List Code Values", description = "Returns the list of Code Values for a given Code\n" + "\n"
+ "Example Requests:\n" + "\n" + "codes/1/codevalues", parameters = @Parameter(name = "codeId", description = "co"))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "A List of code values for a given code", content = @Content(array = @ArraySchema(schema = @Schema(implementation = CodeValuesApiResourceSwagger.GetCodeValuesDataResponse.class)))) })
public List<CodeValueData> retrieveAllCodeValues(@Context final UriInfo uriInfo,
@PathParam("codeName") @Parameter(description = "codeName") final String codeName) {
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
return this.readPlatformService.retrieveAllCodeValues(codeName);
}
@GET
@Path("name/{codeName}/codevalues/{codeValueId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve a Code description", description = "Returns the details of a Code Value\n" + "\n" + "Example Requests:\n"
+ "\n" + "codes/name/ADDRESS_TYPE/codevalues/1")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = CodeValuesApiResourceSwagger.GetCodeValuesDataResponse.class))) })
public CodeValueData retrieveCodeValue(@Context final UriInfo uriInfo,
@PathParam("codeName") @Parameter(description = "codeName") final String codeName,
@PathParam("codeValueId") @Parameter(description = "codeValueId") final Long codeValueId) {
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
return this.readPlatformService.retrieveCodeValue(codeName, codeValueId);
}
@POST
@Path("name/{codeName}/codevalues")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Create a Code description", description = "")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = CodeValuesApiResourceSwagger.PostCodeValuesDataRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = CodeValuesApiResourceSwagger.PostCodeValueDataResponse.class))) })
public CommandProcessingResult createCodeValue(@PathParam("codeName") @Parameter(description = "codeName") final String codeName,
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
CodeData code = codeReadPlatformService.retrieveCode(codeName);
final CommandWrapper commandRequest = new CommandWrapperBuilder().createCodeValue(code.getId(), codeName)
.withJson(apiRequestBodyAsJson).build();
return this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
@PUT
@Path("name/{codeName}/codevalues/{codeValueId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Update a Code description", description = "Updates the details of a code description.")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = CodeValuesApiResourceSwagger.PutCodeValuesDataRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = CodeValuesApiResourceSwagger.PutCodeValueDataResponse.class))) })
public CommandProcessingResult updateCodeValue(@PathParam("codeName") @Parameter(description = "codeName") final String codeName,
@PathParam("codeValueId") @Parameter(description = "codeValueId") final Long codeValueId,
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
CodeData code = codeReadPlatformService.retrieveCode(codeName);
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateCodeValue(codeName, code.getId(), codeValueId)
.withJson(apiRequestBodyAsJson).build();
return this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
@DELETE
@Path("name/{codeName}/codevalues/{codeValueId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Delete a Code description", description = "Deletes a code description")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = CodeValuesApiResourceSwagger.DeleteCodeValueDataResponse.class))) })
public CommandProcessingResult deleteCodeValue(@PathParam("codeName") @Parameter(description = "codeName") final String codeName,
@PathParam("codeValueId") @Parameter(description = "codeValueId") final Long codeValueId) {
CodeData code = codeReadPlatformService.retrieveCode(codeName);
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteCodeValue(codeName, code.getId(), codeValueId).build();
return this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
}
@@ -134,7 +134,7 @@ public class CodesApiResource {
public String retrieveCodeByName(@PathParam("codeName") @Parameter(description = "codeName") final String codeName,
@Context final UriInfo uriInfo) {
final CodeData code = this.readPlatformService.retriveCode(codeName);
final CodeData code = this.readPlatformService.retrieveCode(codeName);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, code, RESPONSE_DATA_PARAMETERS);
@@ -86,7 +86,7 @@ public class CodeReadPlatformServiceImpl implements CodeReadPlatformService {
}
@Override
public CodeData retriveCode(final String codeName) {
public CodeData retrieveCode(final String codeName) {
try {
this.context.authenticatedUser();
@@ -22,10 +22,12 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository;
import org.apache.fineract.infrastructure.codes.exception.CodeValueNotFoundException;
import org.apache.fineract.infrastructure.codes.mapper.CodeValueMapper;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -33,16 +35,13 @@ import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class CodeValueReadPlatformServiceImpl implements CodeValueReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final PlatformSecurityContext context;
@Autowired
public CodeValueReadPlatformServiceImpl(final PlatformSecurityContext context, final JdbcTemplate jdbcTemplate) {
this.context = context;
this.jdbcTemplate = jdbcTemplate;
}
private final CodeValueRepository codeValueRepository;
private final CodeValueMapper codeValueMapper;
private static final class CodeValueDataMapper implements RowMapper<CodeValueData> {
@@ -105,4 +104,22 @@ public class CodeValueReadPlatformServiceImpl implements CodeValueReadPlatformSe
}
}
@Override
public List<CodeValueData> retrieveAllCodeValues(String codeName) {
// TODO: review whether it is needed.
this.context.authenticatedUser();
return codeValueMapper.map(codeValueRepository.findByCodeName(codeName));
}
@Override
public CodeValueData retrieveCodeValue(String codeName, Long codeValueId) {
// TODO: review whether it is needed.
this.context.authenticatedUser();
CodeValueData codeValueData = codeValueMapper.map(codeValueRepository.findByCodeNameAndId(codeName, codeValueId));
if (codeValueData == null) {
throw new CodeValueNotFoundException(codeName, codeValueId);
}
return codeValueData;
}
}
@@ -689,7 +689,7 @@ public class DatatableWriteServiceImpl implements DatatableWriteService {
if (StringUtils.isNotBlank(code)) {
if (isConstraintApproach) {
codeMappings.put(dataTableNameAlias + "_" + name, this.codeReadPlatformService.retriveCode(code).getId());
codeMappings.put(dataTableNameAlias + "_" + name, this.codeReadPlatformService.retrieveCode(code).getId());
String fkName = "fk_" + dataTableNameAlias + "_" + name;
constrainBuilder.append(", CONSTRAINT ").append(sqlGenerator.escape(fkName)).append(" ").append("FOREIGN KEY (")
.append(sqlGenerator.escape(name)).append(") ").append(REFERENCES_CLAUSE)
@@ -753,7 +753,7 @@ public class DatatableWriteServiceImpl implements DatatableWriteService {
if (StringUtils.isNotBlank(code)) {
if (isConstraintApproach) {
String fkName = "fk_" + dataTableNameAlias + "_" + name;
codeMappings.put(dataTableNameAlias + "_" + name, this.codeReadPlatformService.retriveCode(code).getId());
codeMappings.put(dataTableNameAlias + "_" + name, this.codeReadPlatformService.retrieveCode(code).getId());
constrainBuilder.append(",ADD CONSTRAINT ").append(sqlGenerator.escape(fkName)).append(" ").append("FOREIGN KEY (")
.append(sqlGenerator.escape(name)).append(") ").append(REFERENCES_CLAUSE)
.append(sqlGenerator.escape(CODE_VALUES_TABLE)).append(" (").append(TABLE_FIELD_ID).append(")");
@@ -823,7 +823,7 @@ public class DatatableWriteServiceImpl implements DatatableWriteService {
}
}
if (newCode != null) {
codeMappings.put(dataTableNameAlias + "_" + newName, this.codeReadPlatformService.retriveCode(newCode).getId());
codeMappings.put(dataTableNameAlias + "_" + newName, this.codeReadPlatformService.retrieveCode(newCode).getId());
if (code == null || !StringUtils.equalsIgnoreCase(oldName, newName)) {
constrainBuilder.append(", ADD CONSTRAINT ").append(sqlGenerator.escape(newFkName)).append(" ")
.append("FOREIGN KEY (").append(sqlGenerator.escape(newName)).append(") ").append(REFERENCES_CLAUSE)