FINERACT-2169: Audits API recourse refactor and clean up;

This commit is contained in:
viktorpavlenko
2025-03-14 18:08:59 +00:00
committed by Adam Saghy
parent 1d72d4cfb8
commit d96e78a4f7
8 changed files with 137 additions and 123 deletions
@@ -20,12 +20,8 @@ package org.apache.fineract.commands.api;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.BeanParam;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@@ -35,21 +31,16 @@ import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.UriInfo;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.commands.data.AuditData;
import org.apache.fineract.commands.data.AuditSearchData;
import org.apache.fineract.commands.data.request.AuditRequest;
import org.apache.fineract.commands.service.AuditReadPlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.PaginationParameters;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.infrastructure.security.service.SqlValidator;
import org.apache.fineract.infrastructure.security.utils.SQLBuilder;
import org.springframework.stereotype.Component;
@@ -63,18 +54,12 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor
public class AuditsApiResource {
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList("id", "actionName", "entityName", "resourceId",
"subresourceId", "maker", "madeOnDate", "checker", "checkedOnDate", "processingResult", "commandAsJson", "officeName",
"groupLevelName", "groupName", "clientName", "loanAccountNo", "savingsAccountNo", "clientId", "loanId", "url"));
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "AUDIT";
private final PlatformSecurityContext context;
private final AuditReadPlatformService auditReadPlatformService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final DefaultToApiJsonSerializer<AuditData> toApiJsonSerializer;
private final DefaultToApiJsonSerializer<AuditSearchData> toApiJsonSerializerSearchTemplate;
private final SqlValidator sqlValidator;
private final ToApiJsonSerializer<String> toApiJsonSerializer;
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@@ -83,50 +68,22 @@ public class AuditsApiResource {
+ "\n" + "Example Requests:\n" + "\n" + "audits\n" + "\n" + "audits?fields=madeOnDate,maker,processingResult\n" + "\n"
+ "audits?makerDateTimeFrom=2013-03-25 08:00:00&makerDateTimeTo=2013-04-04 18:00:00\n" + "\n" + "audits?officeId=1\n" + "\n"
+ "audits?officeId=1&includeJson=true")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = MakercheckersApiResourceSwagger.GetMakerCheckerResponse.class)))) })
public String retrieveAuditEntries(@Context final UriInfo uriInfo,
@QueryParam("actionName") @Parameter(description = "actionName") final String actionName,
@QueryParam("entityName") @Parameter(description = "entityName") final String entityName,
@QueryParam("resourceId") @Parameter(description = "resourceId") final Long resourceId,
@QueryParam("makerId") @Parameter(description = "makerId") final Long makerId,
@QueryParam("makerDateTimeFrom") @Parameter(description = "makerDateTimeFrom") final String makerDateTimeFrom,
@QueryParam("makerDateTimeTo") @Parameter(description = "makerDateTimeTo") final String makerDateTimeTo,
@QueryParam("checkerId") @Parameter(description = "checkerId") final Long checkerId,
@QueryParam("checkerDateTimeFrom") @Parameter(description = "checkerDateTimeFrom") final String checkerDateTimeFrom,
@QueryParam("checkerDateTimeTo") @Parameter(description = "checkerDateTimeTo") final String checkerDateTimeTo,
@QueryParam("processingResult") @Parameter(description = "processingResult") final Integer processingResult,
@QueryParam("officeId") @Parameter(description = "officeId") final Integer officeId,
@QueryParam("groupId") @Parameter(description = "groupId") final Integer groupId,
@QueryParam("clientId") @Parameter(description = "clientId") final Integer clientId,
@QueryParam("loanid") @Parameter(description = "loanid") final Integer loanId,
@QueryParam("savingsAccountId") @Parameter(description = "savingsAccountId") final Integer savingsAccountId,
@QueryParam("paged") @Parameter(description = "paged") final Boolean paged,
public String retrieveAuditEntries(@Context final UriInfo uriInfo, @BeanParam AuditRequest auditRequest,
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) {
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder,
@QueryParam("paged") @Parameter(description = "paged") final Boolean paged) {
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
sqlValidator.validate(orderBy);
sqlValidator.validate(sortOrder);
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
final PaginationParameters parameters = PaginationParameters.builder().paged(Boolean.TRUE.equals(paged)).limit(limit).offset(offset)
.orderBy(orderBy).sortOrder(sortOrder).build();
final SQLBuilder extraCriteria = getExtraCriteria(actionName, entityName, resourceId, makerId, makerDateTimeFrom, makerDateTimeTo,
checkerId, checkerDateTimeFrom, checkerDateTimeTo, processingResult, officeId, groupId, clientId, loanId, savingsAccountId);
final SQLBuilder extraCriteria = getExtraCriteria(auditRequest);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
if (parameters.isPaged()) {
final Page<AuditData> auditEntries = this.auditReadPlatformService.retrievePaginatedAuditEntries(extraCriteria,
settings.isIncludeJson(), parameters);
return this.toApiJsonSerializer.serialize(settings, auditEntries, RESPONSE_DATA_PARAMETERS);
}
final Collection<AuditData> auditEntries = this.auditReadPlatformService.retrieveAuditEntries(extraCriteria,
settings.isIncludeJson());
return this.toApiJsonSerializer.serialize(settings, auditEntries, RESPONSE_DATA_PARAMETERS);
return toApiJsonSerializer.serialize(parameters.isPaged()
? auditReadPlatformService.retrievePaginatedAuditEntries(extraCriteria, settings.isIncludeJson(), parameters)
: auditReadPlatformService.retrieveAuditEntries(extraCriteria, settings.isIncludeJson()));
}
@GET
@@ -135,17 +92,10 @@ public class AuditsApiResource {
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve an Audit Entry", description = "Example Requests:\n" + "\n" + "audits/20\n"
+ "audits/20?fields=madeOnDate,maker,processingResult")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = MakercheckersApiResourceSwagger.GetMakerCheckerResponse.class))) })
public String retrieveAuditEntry(@PathParam("auditId") @Parameter(description = "auditId") final Long auditId,
@Context final UriInfo uriInfo) {
public AuditData retrieveAuditEntry(@PathParam("auditId") @Parameter final Long auditId) {
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
return auditReadPlatformService.retrieveAuditEntry(auditId);
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
final AuditData auditEntry = this.auditReadPlatformService.retrieveAuditEntry(auditId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, auditEntry, RESPONSE_DATA_PARAMETERS);
}
@GET
@@ -154,45 +104,31 @@ public class AuditsApiResource {
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Audit Search Template", description = "This is a convenience resource. It can be useful when building an Audit Search UI. \"appUsers\" are data scoped to the office/branch the requestor is associated with.\n"
+ "\n" + "Example Requests:\n" + "\n" + "audits/searchtemplate\n" + "audits/searchtemplate?fields=actionNames")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = MakercheckersApiResourceSwagger.GetMakerCheckersSearchTemplateResponse.class))) })
public String retrieveAuditSearchTemplate(@Context final UriInfo uriInfo) {
public AuditSearchData retrieveAuditSearchTemplate() {
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
final AuditSearchData auditSearchData = this.auditReadPlatformService.retrieveSearchTemplate("audit");
final Set<String> RESPONSE_DATA_PARAMETERS_SEARCH_TEMPLATE = new HashSet<>(
Arrays.asList("appUsers", "actionNames", "entityNames", "status"));
return this.toApiJsonSerializerSearchTemplate.serialize(settings, auditSearchData, RESPONSE_DATA_PARAMETERS_SEARCH_TEMPLATE);
return this.auditReadPlatformService.retrieveSearchTemplate("audit");
}
private SQLBuilder getExtraCriteria(final String actionName, final String entityName, final Long resourceId, final Long makerId,
final String makerDateTimeFrom, final String makerDateTimeTo, final Long checkerId, final String checkerDateTimeFrom,
final String checkerDateTimeTo, final Integer status, final Integer officeId, final Integer groupId, final Integer clientId,
final Integer loanId, final Integer savingsAccountId) {
private SQLBuilder getExtraCriteria(AuditRequest auditRequest) {
SQLBuilder extraCriteria = new SQLBuilder();
extraCriteria.addNonNullCriteria("aud.action_name = ", actionName);
if (entityName != null) {
extraCriteria.addCriteria("aud.entity_name like", entityName + "%");
extraCriteria.addNonNullCriteria("aud.action_name = ", auditRequest.getActionName());
if (auditRequest.getEntityName() != null) {
extraCriteria.addCriteria("aud.entity_name like", auditRequest.getEntityName() + "%");
}
extraCriteria.addNonNullCriteria("aud.resource_id = ", resourceId);
extraCriteria.addNonNullCriteria("aud.maker_id = ", makerId);
extraCriteria.addNonNullCriteria("aud.checker_id = ", checkerId);
extraCriteria.addNonNullCriteria("aud.made_on_date >= ", makerDateTimeFrom);
extraCriteria.addNonNullCriteria("aud.made_on_date <= ", makerDateTimeTo);
extraCriteria.addNonNullCriteria("aud.checked_on_date >= ", checkerDateTimeFrom);
extraCriteria.addNonNullCriteria("aud.checked_on_date <= ", checkerDateTimeTo);
extraCriteria.addNonNullCriteria("aud.status = ", status);
extraCriteria.addNonNullCriteria("aud.office_id = ", officeId);
extraCriteria.addNonNullCriteria("aud.group_id = ", groupId);
extraCriteria.addNonNullCriteria("aud.client_id = ", clientId);
extraCriteria.addNonNullCriteria("aud.loan_id = ", loanId);
extraCriteria.addNonNullCriteria("aud.savings_account_id = ", savingsAccountId);
extraCriteria.addNonNullCriteria("aud.resource_id = ", auditRequest.getResourceId());
extraCriteria.addNonNullCriteria("aud.maker_id = ", auditRequest.getMakerId());
extraCriteria.addNonNullCriteria("aud.checker_id = ", auditRequest.getCheckerId());
extraCriteria.addNonNullCriteria("aud.made_on_date >= ", auditRequest.getMakerDateTimeFrom());
extraCriteria.addNonNullCriteria("aud.made_on_date <= ", auditRequest.getMakerDateTimeTo());
extraCriteria.addNonNullCriteria("aud.checked_on_date >= ", auditRequest.getCheckerDateTimeFrom());
extraCriteria.addNonNullCriteria("aud.checked_on_date <= ", auditRequest.getCheckerDateTimeTo());
extraCriteria.addNonNullCriteria("aud.status = ", auditRequest.getStatus());
extraCriteria.addNonNullCriteria("aud.office_id = ", auditRequest.getOfficeId());
extraCriteria.addNonNullCriteria("aud.group_id = ", auditRequest.getGroupId());
extraCriteria.addNonNullCriteria("aud.client_id = ", auditRequest.getClientId());
extraCriteria.addNonNullCriteria("aud.loan_id = ", auditRequest.getLoanId());
extraCriteria.addNonNullCriteria("aud.savings_account_id = ", auditRequest.getSavingsAccountId());
return extraCriteria;
}
@@ -18,6 +18,8 @@
*/
package org.apache.fineract.commands.data;
import java.io.Serial;
import java.io.Serializable;
import java.time.ZonedDateTime;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -28,7 +30,10 @@ import lombok.Setter;
*/
@AllArgsConstructor
@Getter
public final class AuditData {
public final class AuditData implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private final Long id;
private final String actionName;
@@ -18,21 +18,20 @@
*/
package org.apache.fineract.commands.data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.useradministration.data.AppUserData;
/**
* Immutable data object representing audit search results.
*/
@RequiredArgsConstructor
@Getter
public final class AuditSearchData {
private final Collection<AppUserData> appUsers;
private final List<String> actionNames;
private final List<String> entityNames;
private final Collection<ProcessingResultLookup> statuses;
public record AuditSearchData(Collection<AppUserData> appUsers, List<String> actionNames, List<String> entityNames,
Collection<ProcessingResultLookup> statuses) implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
}
@@ -18,17 +18,15 @@
*/
package org.apache.fineract.commands.data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
* Immutable data object for application user data.
*/
@RequiredArgsConstructor
@Getter
public class ProcessingResultLookup {
public record ProcessingResultLookup(Long id, String processingResult) implements Serializable {
private final Long id;
private final String processingResult;
@Serial
private static final long serialVersionUID = 1L;
}
@@ -0,0 +1,70 @@
/**
* 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.commands.data.request;
import jakarta.ws.rs.QueryParam;
import java.io.Serial;
import java.io.Serializable;
import java.time.ZonedDateTime;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@NoArgsConstructor
public class AuditRequest implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@QueryParam("actionName")
private String actionName;
@QueryParam("entityName")
private String entityName;
@QueryParam("resourceId")
private Long resourceId;
@QueryParam("makerId")
private Long makerId;
@QueryParam("makerDateTimeFrom")
private ZonedDateTime makerDateTimeFrom;
@QueryParam("makerDateTimeTo")
private ZonedDateTime makerDateTimeTo;
@QueryParam("checkerId")
private Long checkerId;
@QueryParam("checkerDateTimeFrom")
private ZonedDateTime checkerDateTimeFrom;
@QueryParam("checkerDateTimeTo")
private ZonedDateTime checkerDateTimeTo;
@QueryParam("status")
private String status;
@QueryParam("clientId")
private Long clientId;
@QueryParam("loanId")
private Long loanId;
@QueryParam("officeId")
private Long officeId;
@QueryParam("groupId")
private Long groupId;
@QueryParam("savingsAccountId")
private Long savingsAccountId;
@QueryParam("processingResult")
private String processingResult;
}
@@ -18,7 +18,7 @@
*/
package org.apache.fineract.commands.service;
import java.util.Collection;
import java.util.List;
import org.apache.fineract.commands.data.AuditData;
import org.apache.fineract.commands.data.AuditSearchData;
import org.apache.fineract.infrastructure.core.data.PaginationParameters;
@@ -27,11 +27,11 @@ import org.apache.fineract.infrastructure.security.utils.SQLBuilder;
public interface AuditReadPlatformService {
Collection<AuditData> retrieveAuditEntries(SQLBuilder extraCriteria, boolean includeJson);
List<AuditData> retrieveAuditEntries(SQLBuilder extraCriteria, boolean includeJson);
Page<AuditData> retrievePaginatedAuditEntries(SQLBuilder extraCriteria, boolean includeJson, PaginationParameters parameters);
Collection<AuditData> retrieveAllEntriesToBeChecked(SQLBuilder extraCriteria, boolean includeJson);
List<AuditData> retrieveAllEntriesToBeChecked(SQLBuilder extraCriteria, boolean includeJson);
AuditData retrieveAuditEntry(Long auditId);
@@ -47,6 +47,7 @@ import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.PaginationHelper;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.infrastructure.security.service.SqlValidator;
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
import org.apache.fineract.infrastructure.security.utils.SQLBuilder;
import org.apache.fineract.organisation.office.data.OfficeData;
@@ -92,6 +93,7 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService {
private final SavingsProductReadPlatformService savingsProductReadPlatformService;
private final DepositProductReadPlatformService depositProductReadPlatformService;
private final ColumnValidator columnValidator;
private final SqlValidator sqlValidator;
private static final class AuditMapper implements RowMapper<AuditData> {
@@ -167,7 +169,7 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService {
}
@Override
public Collection<AuditData> retrieveAuditEntries(final SQLBuilder extraCriteria, final boolean includeJson) {
public List<AuditData> retrieveAuditEntries(final SQLBuilder extraCriteria, final boolean includeJson) {
return retrieveEntries("audit", extraCriteria, " order by aud.id DESC limit " + PaginationParameters.DEFAULT_MAX_LIMIT,
includeJson);
}
@@ -176,6 +178,8 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService {
public Page<AuditData> retrievePaginatedAuditEntries(final SQLBuilder extraCriteria, final boolean includeJson,
final PaginationParameters parameters) {
sqlValidator.validate(parameters.getOrderBy());
sqlValidator.validate(parameters.getSortOrder());
this.paginationParametersDataValidator.validateParameterValues(parameters, supportedOrderByValues, "audits");
final AppUser currentUser = this.context.authenticatedUser();
final String hierarchy = currentUser.getOffice().getHierarchy();
@@ -203,12 +207,12 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService {
}
@Override
public Collection<AuditData> retrieveAllEntriesToBeChecked(final SQLBuilder extraCriteria, final boolean includeJson) {
public List<AuditData> retrieveAllEntriesToBeChecked(final SQLBuilder extraCriteria, final boolean includeJson) {
extraCriteria.addCriteria("aud.status = ", 2);
return retrieveEntries("makerchecker", extraCriteria, " order by aud.id, mk.username", includeJson);
}
private Collection<AuditData> retrieveEntries(final String useType, final SQLBuilder extraCriteria, final String groupAndOrderBySQL,
private List<AuditData> retrieveEntries(final String useType, final SQLBuilder extraCriteria, final String groupAndOrderBySQL,
final boolean includeJson) {
if ((!useType.equals("audit") && !useType.equals("makerchecker"))) {
@@ -25,6 +25,7 @@ import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.PaginationHelper;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.infrastructure.security.service.SqlValidator;
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
import org.apache.fineract.organisation.staff.service.StaffReadPlatformService;
@@ -50,11 +51,12 @@ public class CommandsConfiguration {
PaginationHelper paginationHelper, DatabaseSpecificSQLGenerator sqlGenerator,
PaginationParametersDataValidator paginationParametersDataValidator,
SavingsProductReadPlatformService savingsProductReadPlatformService,
DepositProductReadPlatformService depositProductReadPlatformService, ColumnValidator columnValidator) {
DepositProductReadPlatformService depositProductReadPlatformService, ColumnValidator columnValidator,
SqlValidator sqlValidator) {
return new AuditReadPlatformServiceImpl(jdbcTemplate, context, fromApiJsonHelper, appUserReadPlatformService,
officeReadPlatformService, clientReadPlatformService, loanProductReadPlatformService, staffReadPlatformService,
paginationHelper, sqlGenerator, paginationParametersDataValidator, savingsProductReadPlatformService,
depositProductReadPlatformService, columnValidator);
depositProductReadPlatformService, columnValidator, sqlValidator);
}
}