Merge pull request #694 from ashok-conflux/MIFOSX-824

Added pagination support to audit API
This commit is contained in:
Vishwas Babu A J
2013-12-12 22:58:37 -08:00
6 changed files with 316 additions and 8 deletions
+90 -2
View File
@@ -10657,8 +10657,8 @@ Content-Type: application/json
<div class="method-section"> <div class="method-section">
<div class="method-description"> <div class="method-description">
<h4>List Audits</h4> <h4>List Audits</h4>
<p>Get a list of audits that match the criteria supplied, <p>Get a 200 list of audits that match the criteria supplied and sorted by audit id in descending order,
and are within the requestors' data scope.</p> and are within the requestors' data scope. Also it supports pagination and sorting</p>
<h5>Arguments</h5> <h5>Arguments</h5>
<dl class="argument-list"> <dl class="argument-list">
<dt>actionName</dt> <dt>actionName</dt>
@@ -10749,6 +10749,35 @@ Content-Type: application/json
<dd>optional, <span> <dd>optional, <span>
Values are true, false. Default is false.</span> Values are true, false. Default is false.</span>
</dd> </dd>
<dt>paged</dt>
<dd>
Boolean <span>optional</span>, defaults to false
</dd>
<dd>If paged is true then results will be paginated.</dd>
<dt>offset</dt>
<dd>
Integer <span>optional</span>, defaults to 0
</dd>
<dd>Indicates from what result to start from.</dd>
<dt>limit</dt>
<dd>
Integer <span>optional</span>, defaults to 200
</dd>
<dd>Restricts the size of results returned. To override the default and return all entries you must explicitly pass a non-positive integer value for limit e.g. limit=0, or limit=-1</dd>
<dt>orderBy</dt>
<dd>
String <span>optional</span>, one of <span>id, actionName, entityName, resourceId, subresourceId, madeOnDate, checkedOnDate, officeName, groupName, clientName, loanAccountNo, savingsAccountNo</span>
</dd>
<dd>Orders the results by the field indicated.</dd>
<dt>sortBy</dt>
<dd>
String <span>optional</span>, one of <span>ASC, DESC</span>
</dd>
<dd>Indicates what way to order results if <i>orderBy</i> is used.</dd>
</dl> </dl>
<p>Example Requests:</p> <p>Example Requests:</p>
@@ -10842,6 +10871,65 @@ GET https://DomainName/api/v1/audits
},... },...
] ]
</code> </code>
<code class="method-declaration">
GET https://DomainName/api/v1/audits?paged=true&limit=5
</code>
<code class="method-response">
{
"totalFilteredRecords": 30,
"pageItems": [
{
"id": 671,
"actionName": "PERMISSIONS",
"entityName": "ROLE",
"resourceId": 6,
"maker": "keithwoodlock",
"madeOnDate": 1365014262000,
"processingResult": "processed"
},
{
"id": 670,
"actionName": "CREATE",
"entityName": "CLIENTNOTE",
"resourceId": 287,
"maker": "keithwoodlock",
"madeOnDate": 1365014204000,
"processingResult": "awaiting.approval",
"officeName": "my office",
"clientName": "gg ggg"
},
{
"id": 668,
"actionName": "UPDATE",
"entityName": "PERMISSION",
"maker": "keithwoodlock",
"madeOnDate": 1365014186000,
"processingResult": "processed"
},
{
"id": 667,
"actionName": "CREATE",
"entityName": "CLIENTNOTE",
"resourceId": 286,
"maker": "keithwoodlock",
"madeOnDate": 1365014169000,
"processingResult": "processed",
"officeName": "my office name",
"clientName": "gg ggg"
},
{
"id": 666,
"actionName": "CREATE",
"entityName": "CLIENT",
"resourceId": 363,
"maker": "keithwoodlock",
"madeOnDate": 1365012843000,
"processingResult": "awaiting.approval",
"officeName": "my office name"
}
]
}
</code>
</div> </div>
</div> </div>
@@ -26,8 +26,10 @@ import org.mifosplatform.commands.data.AuditSearchData;
import org.mifosplatform.commands.service.AuditReadPlatformService; import org.mifosplatform.commands.service.AuditReadPlatformService;
import org.mifosplatform.infrastructure.core.api.ApiParameterHelper; import org.mifosplatform.infrastructure.core.api.ApiParameterHelper;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.data.PaginationParameters;
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
@@ -72,15 +74,22 @@ public class AuditsApiResource {
@QueryParam("checkerDateTimeTo") final String checkerDateTimeTo, @QueryParam("checkerDateTimeTo") final String checkerDateTimeTo,
@QueryParam("processingResult") final Integer processingResult, @QueryParam("officeId") final Integer officeId, @QueryParam("processingResult") final Integer processingResult, @QueryParam("officeId") final Integer officeId,
@QueryParam("groupId") final Integer groupId, @QueryParam("clientId") final Integer clientId, @QueryParam("groupId") final Integer groupId, @QueryParam("clientId") final Integer clientId,
@QueryParam("loanid") final Integer loanId, @QueryParam("savingsAccountId") final Integer savingsAccountId) { @QueryParam("loanid") final Integer loanId, @QueryParam("savingsAccountId") final Integer savingsAccountId,
@QueryParam("paged") final Boolean paged, @QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit,
@QueryParam("orderBy") final String orderBy, @QueryParam("sortOrder") final String sortOrder) {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions); this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
final PaginationParameters parameters = PaginationParameters.instance(paged, offset, limit, orderBy, sortOrder);
final String extraCriteria = getExtraCriteria(actionName, entityName, resourceId, makerId, makerDateTimeFrom, makerDateTimeTo, final String extraCriteria = getExtraCriteria(actionName, entityName, resourceId, makerId, makerDateTimeFrom, makerDateTimeTo,
checkerId, checkerDateTimeFrom, checkerDateTimeTo, processingResult, officeId, groupId, clientId, loanId, savingsAccountId); checkerId, checkerDateTimeFrom, checkerDateTimeTo, processingResult, officeId, groupId, clientId, loanId, savingsAccountId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); 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, this.RESPONSE_DATA_PARAMETERS);
}
final Collection<AuditData> auditEntries = this.auditReadPlatformService.retrieveAuditEntries(extraCriteria, final Collection<AuditData> auditEntries = this.auditReadPlatformService.retrieveAuditEntries(extraCriteria,
settings.isIncludeJson()); settings.isIncludeJson());
@@ -9,10 +9,14 @@ import java.util.Collection;
import org.mifosplatform.commands.data.AuditData; import org.mifosplatform.commands.data.AuditData;
import org.mifosplatform.commands.data.AuditSearchData; import org.mifosplatform.commands.data.AuditSearchData;
import org.mifosplatform.infrastructure.core.data.PaginationParameters;
import org.mifosplatform.infrastructure.core.service.Page;
public interface AuditReadPlatformService { public interface AuditReadPlatformService {
Collection<AuditData> retrieveAuditEntries(String extraCriteria, boolean includeJson); Collection<AuditData> retrieveAuditEntries(String extraCriteria, boolean includeJson);
Page<AuditData> retrievePaginatedAuditEntries(String extraCriteria, boolean includeJson, PaginationParameters parameters);
Collection<AuditData> retrieveAllEntriesToBeChecked(String extraCriteria, boolean includeJson); Collection<AuditData> retrieveAllEntriesToBeChecked(String extraCriteria, boolean includeJson);
@@ -8,18 +8,25 @@ package org.mifosplatform.commands.service;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.mifosplatform.commands.data.AuditData; import org.mifosplatform.commands.data.AuditData;
import org.mifosplatform.commands.data.AuditSearchData; import org.mifosplatform.commands.data.AuditSearchData;
import org.mifosplatform.commands.data.ProcessingResultLookup; import org.mifosplatform.commands.data.ProcessingResultLookup;
import org.mifosplatform.infrastructure.core.data.PaginationParameters;
import org.mifosplatform.infrastructure.core.data.PaginationParametersDataValidator;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.infrastructure.core.service.PaginationHelper;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource; import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.office.data.OfficeData; import org.mifosplatform.organisation.office.data.OfficeData;
@@ -49,6 +56,9 @@ import com.google.gson.JsonObject;
public class AuditReadPlatformServiceImpl implements AuditReadPlatformService { public class AuditReadPlatformServiceImpl implements AuditReadPlatformService {
private final static Logger logger = LoggerFactory.getLogger(AuditReadPlatformServiceImpl.class); private final static Logger logger = LoggerFactory.getLogger(AuditReadPlatformServiceImpl.class);
private final static Set<String> supportedOrderByValues = new HashSet<String>(Arrays.asList("id", "actionName", "entityName",
"resourceId", "subresourceId", "madeOnDate", "checkedOnDate", "officeName", "groupName", "clientName", "loanAccountNo",
"savingsAccountNo"));
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
@@ -58,12 +68,15 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService {
private final ClientReadPlatformService clientReadPlatformService; private final ClientReadPlatformService clientReadPlatformService;
private final LoanProductReadPlatformService loanProductReadPlatformService; private final LoanProductReadPlatformService loanProductReadPlatformService;
private final StaffReadPlatformService staffReadPlatformService; private final StaffReadPlatformService staffReadPlatformService;
private final PaginationHelper<AuditData> paginationHelper = new PaginationHelper<AuditData>();
private final PaginationParametersDataValidator paginationParametersDataValidator;
@Autowired @Autowired
public AuditReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, public AuditReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource,
final FromJsonHelper fromApiJsonHelper, final AppUserReadPlatformService appUserReadPlatformService, final FromJsonHelper fromApiJsonHelper, final AppUserReadPlatformService appUserReadPlatformService,
final OfficeReadPlatformService officeReadPlatformService, final ClientReadPlatformService clientReadPlatformService, final OfficeReadPlatformService officeReadPlatformService, final ClientReadPlatformService clientReadPlatformService,
final LoanProductReadPlatformService loanProductReadPlatformService, final StaffReadPlatformService staffReadPlatformService) { final LoanProductReadPlatformService loanProductReadPlatformService, final StaffReadPlatformService staffReadPlatformService,
final PaginationParametersDataValidator paginationParametersDataValidator) {
this.context = context; this.context = context;
this.jdbcTemplate = new JdbcTemplate(dataSource); this.jdbcTemplate = new JdbcTemplate(dataSource);
this.fromApiJsonHelper = fromApiJsonHelper; this.fromApiJsonHelper = fromApiJsonHelper;
@@ -72,6 +85,7 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService {
this.clientReadPlatformService = clientReadPlatformService; this.clientReadPlatformService = clientReadPlatformService;
this.loanProductReadPlatformService = loanProductReadPlatformService; this.loanProductReadPlatformService = loanProductReadPlatformService;
this.staffReadPlatformService = staffReadPlatformService; this.staffReadPlatformService = staffReadPlatformService;
this.paginationParametersDataValidator = paginationParametersDataValidator;
} }
private static final class AuditMapper implements RowMapper<AuditData> { private static final class AuditMapper implements RowMapper<AuditData> {
@@ -145,10 +159,44 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService {
if (StringUtils.isNotBlank(extraCriteria)) { if (StringUtils.isNotBlank(extraCriteria)) {
updatedExtraCriteria = " where (" + extraCriteria + ")"; updatedExtraCriteria = " where (" + extraCriteria + ")";
} }
updatedExtraCriteria += " order by aud.id DESC"; updatedExtraCriteria += " order by aud.id DESC limit " + PaginationParameters.getCheckedLimit(null);
return retrieveEntries("audit", updatedExtraCriteria, includeJson); return retrieveEntries("audit", updatedExtraCriteria, includeJson);
} }
@Override
public Page<AuditData> retrievePaginatedAuditEntries(final String extraCriteria, final boolean includeJson, final PaginationParameters parameters) {
this.paginationParametersDataValidator.validateParameterValues(parameters, supportedOrderByValues, "audits");
final AppUser currentUser = this.context.authenticatedUser();
final String hierarchy = currentUser.getOffice().getHierarchy();
String updatedExtraCriteria = "";
if (StringUtils.isNotBlank(extraCriteria)) {
updatedExtraCriteria = " where (" + extraCriteria + ")";
}
final AuditMapper rm = new AuditMapper();
final StringBuilder sqlBuilder = new StringBuilder(200);
sqlBuilder.append("select SQL_CALC_FOUND_ROWS ");
sqlBuilder.append(rm.schema(includeJson, hierarchy));
sqlBuilder.append(' ').append(updatedExtraCriteria);
if (parameters.isOrderByRequested()) {
sqlBuilder.append(' ').append(parameters.orderBySql());
}else {
sqlBuilder.append(' ').append(' ').append(" order by aud.id DESC");
}
if (parameters.isLimited()) {
sqlBuilder.append(' ').append(parameters.limitSql());
}
logger.info("sql: " + sqlBuilder.toString());
final String sqlCountRows = "SELECT FOUND_ROWS()";
return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), new Object[] {}, rm);
}
@Override @Override
public Collection<AuditData> retrieveAllEntriesToBeChecked(final String extraCriteria, final boolean includeJson) { public Collection<AuditData> retrieveAllEntriesToBeChecked(final String extraCriteria, final boolean includeJson) {
@@ -195,7 +243,7 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService {
return this.jdbcTemplate.query(sql, rm, new Object[] {}); return this.jdbcTemplate.query(sql, rm, new Object[] {});
} }
@Override @Override
public AuditData retrieveAuditEntry(final Long auditId) { public AuditData retrieveAuditEntry(final Long auditId) {
@@ -0,0 +1,115 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.infrastructure.core.data;
import org.apache.commons.lang.StringUtils;
/**
* <p>
* Immutable data object representing pagination parameter values.
* </p>
*/
public class PaginationParameters {
private final boolean paged;
private final Integer offset;
private final Integer limit;
private final String orderBy;
private final String sortOrder;
public static PaginationParameters instance (Boolean paged, Integer offset, Integer limit, String orderBy, String sortOrder){
if( null == paged) {
paged = false;
}
final Integer maxLimitAllowed = getCheckedLimit(limit);
return new PaginationParameters(paged, offset, maxLimitAllowed, orderBy, sortOrder);
}
private PaginationParameters(boolean paged, Integer offset, Integer limit, String orderBy, String sortOrder) {
this.paged = paged;
this.offset = offset;
this.limit = limit;
this.orderBy = orderBy;
this.sortOrder = sortOrder;
}
public static Integer getCheckedLimit(final Integer limit) {
final Integer maxLimitAllowed = 200;
// default to max limit first off
Integer checkedLimit = maxLimitAllowed;
if (limit != null && limit > 0) {
checkedLimit = limit;
} else if (limit != null) {
// unlimited case: limit provided and 0 or less
checkedLimit = null;
}
return checkedLimit;
}
public boolean isPaged() {
return this.paged;
}
public Integer getOffset() {
return this.offset;
}
public Integer getLimit() {
return this.limit;
}
public String getOrderBy() {
return this.orderBy;
}
public String getSortOrder() {
return this.sortOrder;
}
public boolean isOrderByRequested() {
return StringUtils.isNotBlank(this.orderBy);
}
public boolean isSortOrderProvided() {
return StringUtils.isNotBlank(this.sortOrder);
}
public boolean isLimited() {
return this.limit != null && this.limit.intValue() > 0;
}
public boolean isOffset() {
return this.offset != null;
}
public String orderBySql(){
final StringBuffer sql = new StringBuffer();
if (this.isOrderByRequested()) {
sql.append(" order by ").append(this.getOrderBy());
if (this.isSortOrderProvided()) {
sql.append(' ').append(this.getSortOrder());
}
}
return sql.toString();
}
public String limitSql(){
final StringBuffer sql = new StringBuffer();
if (this.isLimited()) {
sql.append(" limit ").append(this.getLimit());
if (this.isOffset()) {
sql.append(" offset ").append(this.getOffset());
}
}
return sql.toString();
}
}
@@ -0,0 +1,44 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.infrastructure.core.data;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.springframework.stereotype.Component;
@Component
public class PaginationParametersDataValidator {
public static Set<String> sortOrderValues = new HashSet<String>(Arrays.asList("ASC", "DESC"));
public void validateParameterValues(PaginationParameters parameters, final Set<String> supportedOrdeByValues, final String resourceName) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
if (parameters.isOrderByRequested() && !supportedOrdeByValues.contains(parameters.getOrderBy())){
final String defaultUserMessage = "The orderBy value '" + parameters.getOrderBy() + "' is not supported. The supported orderBy values are " + supportedOrdeByValues.toString();
final ApiParameterError error = ApiParameterError.parameterError("validation.msg."+resourceName+".orderBy.value.is.not.supported",
defaultUserMessage, "orderBy", parameters.getOrderBy(), supportedOrdeByValues.toString());
dataValidationErrors.add(error);
}
if (parameters.isSortOrderProvided() && !sortOrderValues.contains(parameters.getSortOrder().toUpperCase())){
final String defaultUserMessage = "The sortOrder value '" + parameters.getSortOrder() + "' is not supported. The supported sortOrder values are " + sortOrderValues.toString();
final ApiParameterError error = ApiParameterError.parameterError("validation.msg."+resourceName+".sortOrder.value.is.not.supported",
defaultUserMessage, "sortOrder", parameters.getSortOrder(), sortOrderValues.toString());
dataValidationErrors.add(error);
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}