MIFOSX-97: process role commands through command_source table.

This commit is contained in:
Keith Woodlock
2012-11-22 14:51:53 +00:00
parent 85de5e92c2
commit 79ff5a7511
17 changed files with 1162 additions and 1054 deletions
@@ -89,14 +89,10 @@ public class ClientsApiResource {
@GET @GET
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllIndividualClients(@Context final UriInfo uriInfo, public String retrieveAllIndividualClients(@Context final UriInfo uriInfo, @QueryParam("sqlSearch") final String sqlSearch,
@QueryParam("sqlSearch") final String sqlSearch, @QueryParam("officeId") final Integer officeId, @QueryParam("externalId") final String externalId,
@QueryParam("officeId") final Integer officeId, @QueryParam("displayName") final String displayName, @QueryParam("firstName") final String firstName,
@QueryParam("externalId") final String externalId, @QueryParam("lastName") final String lastName, @QueryParam("underHierarchy") final String hierarchy) {
@QueryParam("displayName") final String displayName,
@QueryParam("firstName") final String firstName,
@QueryParam("lastName") final String lastName,
@QueryParam("underHierarchy") final String hierarchy) {
context.authenticatedUser().validateHasReadPermission("CLIENT"); context.authenticatedUser().validateHasReadPermission("CLIENT");
@@ -110,8 +106,7 @@ public class ClientsApiResource {
return this.apiJsonSerializerService.serializeClientDataToJson(prettyPrint, responseParameters, clients); return this.apiJsonSerializerService.serializeClientDataToJson(prettyPrint, responseParameters, clients);
} }
private String getClientCriteria(String sqlSearch, Integer officeId, private String getClientCriteria(String sqlSearch, Integer officeId, String externalId, String displayName, String firstName,
String externalId, String displayName, String firstName,
String lastName, String hierarchy) { String lastName, String hierarchy) {
String extraCriteria = ""; String extraCriteria = "";
@@ -158,9 +153,7 @@ public class ClientsApiResource {
@Path("{clientId}") @Path("{clientId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String retrieveClient( public String retrieveClient(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
@PathParam("clientId") final Long clientId,
@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission("CLIENT"); context.authenticatedUser().validateHasReadPermission("CLIENT");
@@ -201,9 +194,11 @@ public class ClientsApiResource {
boolean retrieveChanges = true; boolean retrieveChanges = true;
if (retrieveChanges) { if (retrieveChanges) {
Collection<CommandSourceData> unprocessedChanges = this.commandSourceReadPlatformService.retrieveUnprocessChangesByResourceId("clients", clientId); Collection<CommandSourceData> unprocessedChanges = this.commandSourceReadPlatformService.retrieveUnprocessChangesByResourceId(
"clients", clientId);
for (CommandSourceData commandSourceData : unprocessedChanges) { for (CommandSourceData commandSourceData : unprocessedChanges) {
ClientData change = this.apiDataConversionService.convertInternalJsonFormatToClientDataChange(clientId, commandSourceData.json()); ClientData change = this.apiDataConversionService.convertInternalJsonFormatToClientDataChange(clientId,
commandSourceData.json());
dataChanges.add(change); dataChanges.add(change);
} }
} }
@@ -240,7 +235,8 @@ public class ClientsApiResource {
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String createClient(final String jsonRequestBody) { public String createClient(final String jsonRequestBody) {
final EntityIdentifier result = this.commandsSourceWritePlatformService.logCommandSource("CREATE", "clients", null, jsonRequestBody); final EntityIdentifier result = this.commandsSourceWritePlatformService
.logCommandSource("CREATE", "clients", null, jsonRequestBody);
return this.apiJsonSerializerService.serializeEntityIdentifier(result); return this.apiJsonSerializerService.serializeEntityIdentifier(result);
} }
@@ -251,7 +247,8 @@ public class ClientsApiResource {
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String updateClient(@PathParam("clientId") final Long clientId, final String jsonRequestBody) { public String updateClient(@PathParam("clientId") final Long clientId, final String jsonRequestBody) {
final EntityIdentifier result = this.commandsSourceWritePlatformService.logCommandSource("UPDATE", "clients", clientId, jsonRequestBody); final EntityIdentifier result = this.commandsSourceWritePlatformService.logCommandSource("UPDATE", "clients", clientId,
jsonRequestBody);
return this.apiJsonSerializerService.serializeEntityIdentifier(result); return this.apiJsonSerializerService.serializeEntityIdentifier(result);
} }
@@ -271,18 +268,16 @@ public class ClientsApiResource {
@Path("{clientId}/loans") @Path("{clientId}/loans")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String retrieveClientAccount(@PathParam("clientId") final Long clientId, public String retrieveClientAccount(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission("CLIENT"); context.authenticatedUser().validateHasReadPermission("CLIENT");
Set<String> typicalResponseParameters = new HashSet<String>( Set<String> typicalResponseParameters = new HashSet<String>(Arrays.asList("pendingApprovalLoans", "awaitingDisbursalLoans",
Arrays.asList("pendingApprovalLoans", "awaitingDisbursalLoans", "openLoans", "closedLoans", "openLoans", "closedLoans", "anyLoanCount", "pendingApprovalLoanCount", "awaitingDisbursalLoanCount", "activeLoanCount",
"anyLoanCount", "pendingApprovalLoanCount", "awaitingDisbursalLoanCount", "activeLoanCount", "closedLoanCount", "closedLoanCount", "pendingApprovalDespositAccountsCount", "pendingApprovalDespositAccounts",
"pendingApprovalDespositAccountsCount", "pendingApprovalDespositAccounts", "approvedDespositAccountsCount", "approvedDespositAccounts", "approvedDespositAccountsCount", "approvedDespositAccounts", "withdrawnByClientDespositAccountsCount",
"withdrawnByClientDespositAccountsCount","withdrawnByClientDespositAccounts","closedDepositAccountsCount","closedDepositAccounts", "withdrawnByClientDespositAccounts", "closedDepositAccountsCount", "closedDepositAccounts", "rejectedDepositAccountsCount",
"rejectedDepositAccountsCount","rejectedDepositAccounts","preclosedDepositAccountsCount","preclosedDepositAccounts") "rejectedDepositAccounts", "preclosedDepositAccountsCount", "preclosedDepositAccounts"));
);
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) { if (responseParameters.isEmpty()) {
@@ -292,7 +287,8 @@ public class ClientsApiResource {
ClientAccountSummaryCollectionData clientAccount = this.clientReadPlatformService.retrieveClientAccountDetails(clientId); ClientAccountSummaryCollectionData clientAccount = this.clientReadPlatformService.retrieveClientAccountDetails(clientId);
return this.apiJsonSerializerService.serializeClientAccountSummaryCollectionDataToJson(prettyPrint, responseParameters, clientAccount); return this.apiJsonSerializerService.serializeClientAccountSummaryCollectionDataToJson(prettyPrint, responseParameters,
clientAccount);
} }
@GET @GET
@@ -303,10 +299,8 @@ public class ClientsApiResource {
context.authenticatedUser().validateHasReadPermission("CLIENTNOTE"); context.authenticatedUser().validateHasReadPermission("CLIENTNOTE");
Set<String> typicalResponseParameters = new HashSet<String>( Set<String> typicalResponseParameters = new HashSet<String>(Arrays.asList("id", "clientId", "loanId", "loanTransactionId",
Arrays.asList("id", "clientId", "loanId", "loanTransactionId", "noteType", "note", "createdById", "createdByUsername", "noteType", "note", "createdById", "createdByUsername", "createdOn", "updatedById", "updatedByUsername", "updatedOn"));
"createdOn", "updatedById", "updatedByUsername", "updatedOn")
);
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) { if (responseParameters.isEmpty()) {
@@ -336,17 +330,13 @@ public class ClientsApiResource {
@Path("{clientId}/notes/{noteId}") @Path("{clientId}/notes/{noteId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String retrieveClientNote( public String retrieveClientNote(@PathParam("clientId") final Long clientId, @PathParam("noteId") final Long noteId,
@PathParam("clientId") final Long clientId,
@PathParam("noteId") final Long noteId,
@Context final UriInfo uriInfo) { @Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission("CLIENTNOTE"); context.authenticatedUser().validateHasReadPermission("CLIENTNOTE");
Set<String> typicalResponseParameters = new HashSet<String>( Set<String> typicalResponseParameters = new HashSet<String>(Arrays.asList("id", "clientId", "loanId", "loanTransactionId",
Arrays.asList("id", "clientId", "loanId", "loanTransactionId", "noteType", "note", "createdById", "createdByUsername", "noteType", "note", "createdById", "createdByUsername", "createdOn", "updatedById", "updatedByUsername", "updatedOn"));
"createdOn", "updatedById", "updatedByUsername", "updatedOn")
);
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) { if (responseParameters.isEmpty()) {
@@ -363,9 +353,8 @@ public class ClientsApiResource {
@Path("{clientId}/notes/{noteId}") @Path("{clientId}/notes/{noteId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public Response updateClientNote( public Response updateClientNote(@PathParam("clientId") final Long clientId, @PathParam("noteId") final Long noteId,
@PathParam("clientId") final Long clientId, final String jsonRequestBody) {
@PathParam("noteId") final Long noteId, final String jsonRequestBody) {
NoteCommand command = this.apiDataConversionService.convertJsonToNoteCommand(noteId, clientId, jsonRequestBody); NoteCommand command = this.apiDataConversionService.convertJsonToNoteCommand(noteId, clientId, jsonRequestBody);
@@ -381,32 +370,25 @@ public class ClientsApiResource {
@Path("{clientId}/image") @Path("{clientId}/image")
@Consumes({ MediaType.MULTIPART_FORM_DATA }) @Consumes({ MediaType.MULTIPART_FORM_DATA })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public Response addNewClientImage( public Response addNewClientImage(@PathParam("clientId") final Long clientId, @HeaderParam("Content-Length") Long fileSize,
@PathParam("clientId") final Long clientId, @FormDataParam("file") InputStream inputStream, @FormDataParam("file") FormDataContentDisposition fileDetails,
@HeaderParam("Content-Length") Long fileSize,
@FormDataParam("file") InputStream inputStream,
@FormDataParam("file") FormDataContentDisposition fileDetails,
@FormDataParam("file") FormDataBodyPart bodyPart) { @FormDataParam("file") FormDataBodyPart bodyPart) {
// TODO: vishwas might need more advances validation (like reading magic
//TODO: vishwas might need more advances validation (like reading magic number) for handling malicious clients // number) for handling malicious clients
// and clients not setting mime type // and clients not setting mime type
FileUtils.validateClientImageNotEmpty(fileDetails.getFileName()); FileUtils.validateClientImageNotEmpty(fileDetails.getFileName());
FileUtils.validateImageMimeType(bodyPart.getMediaType().toString()); FileUtils.validateImageMimeType(bodyPart.getMediaType().toString());
FileUtils.validateFileSizeWithinPermissibleRange(fileSize, FileUtils.validateFileSizeWithinPermissibleRange(fileSize, fileDetails.getFileName(), ApiConstants.MAX_FILE_UPLOAD_SIZE_IN_MB);
fileDetails.getFileName(),
ApiConstants.MAX_FILE_UPLOAD_SIZE_IN_MB);
logger.debug(bodyPart.getMediaType().toString()); logger.debug(bodyPart.getMediaType().toString());
EntityIdentifier entityIdentifier = this.clientWritePlatformService EntityIdentifier entityIdentifier = this.clientWritePlatformService.saveOrUpdateClientImage(clientId, fileDetails.getFileName(),
.saveOrUpdateClientImage(clientId, fileDetails.getFileName(),
inputStream); inputStream);
return Response.ok().entity(entityIdentifier).build(); return Response.ok().entity(entityIdentifier).build();
} }
/** /**
* Upload image as a Data URL (essentially a base64 encoded stream) * Upload image as a Data URL (essentially a base64 encoded stream)
*/ */
@@ -414,15 +396,11 @@ public class ClientsApiResource {
@Path("{clientId}/image") @Path("{clientId}/image")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public Response addNewClientImage( public Response addNewClientImage(@PathParam("clientId") final Long clientId, final String jsonRequestBody) {
@PathParam("clientId") final Long clientId,
final String jsonRequestBody) {
Base64EncodedImage base64EncodedImage = FileUtils Base64EncodedImage base64EncodedImage = FileUtils.extractImageFromDataURL(jsonRequestBody);
.extractImageFromDataURL(jsonRequestBody);
EntityIdentifier entityIdentifier = this.clientWritePlatformService EntityIdentifier entityIdentifier = this.clientWritePlatformService.saveOrUpdateClientImage(clientId, base64EncodedImage);
.saveOrUpdateClientImage(clientId, base64EncodedImage);
return Response.ok().entity(entityIdentifier).build(); return Response.ok().entity(entityIdentifier).build();
} }
@@ -434,36 +412,28 @@ public class ClientsApiResource {
@Path("{clientId}/image") @Path("{clientId}/image")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public Response retrieveClientImage( public Response retrieveClientImage(@PathParam("clientId") final Long clientId) {
@PathParam("clientId") final Long clientId) {
context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE"); context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE");
final ClientData clientData = this.clientReadPlatformService.retrieveIndividualClient(clientId); final ClientData clientData = this.clientReadPlatformService.retrieveIndividualClient(clientId);
if (clientData.imageKeyDoesNotExist()) { if (clientData.imageKeyDoesNotExist()) { throw new ImageNotFoundException("clients", clientId); }
throw new ImageNotFoundException("clients", clientId);
}
return Response.ok().entity(Base64.encodeFromFile(clientData.imageKey())).build(); return Response.ok().entity(Base64.encodeFromFile(clientData.imageKey())).build();
} }
/** /**
* This method is added only for consistency with other URL patterns * This method is added only for consistency with other URL patterns and for
* and for maintaining consistency of usage of the HTTP "verb" * maintaining consistency of usage of the HTTP "verb" at the client side
* at the client side
*/ */
@PUT @PUT
@Path("{clientId}/image") @Path("{clientId}/image")
@Consumes({ MediaType.MULTIPART_FORM_DATA }) @Consumes({ MediaType.MULTIPART_FORM_DATA })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public Response updateClientImage( public Response updateClientImage(@PathParam("clientId") final Long clientId, @HeaderParam("Content-Length") Long fileSize,
@PathParam("clientId") final Long clientId, @FormDataParam("file") InputStream inputStream, @FormDataParam("file") FormDataContentDisposition fileDetails,
@HeaderParam("Content-Length") Long fileSize,
@FormDataParam("file") InputStream inputStream,
@FormDataParam("file") FormDataContentDisposition fileDetails,
@FormDataParam("file") FormDataBodyPart bodyPart) { @FormDataParam("file") FormDataBodyPart bodyPart) {
return addNewClientImage(clientId, fileSize, inputStream, fileDetails, return addNewClientImage(clientId, fileSize, inputStream, fileDetails, bodyPart);
bodyPart);
} }
@DELETE @DELETE
@@ -475,11 +445,9 @@ public class ClientsApiResource {
return Response.ok(new EntityIdentifier(clientId)).build(); return Response.ok(new EntityIdentifier(clientId)).build();
} }
/** /**
* This method is added only for consistency with other URL patterns * This method is added only for consistency with other URL patterns and for
* and for maintaining consistency of usage of the HTTP "verb" * maintaining consistency of usage of the HTTP "verb" at the client side
* at the client side
* *
* Upload image as a Data URL (essentially a base64 encoded stream) * Upload image as a Data URL (essentially a base64 encoded stream)
*/ */
@@ -487,9 +455,7 @@ public class ClientsApiResource {
@Path("{clientId}/image") @Path("{clientId}/image")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public Response updateClientImage( public Response updateClientImage(@PathParam("clientId") final Long clientId, final String jsonRequestBody) {
@PathParam("clientId") final Long clientId,
final String jsonRequestBody) {
return addNewClientImage(clientId, jsonRequestBody); return addNewClientImage(clientId, jsonRequestBody);
} }
} }
@@ -25,6 +25,7 @@ import org.mifosng.platform.api.data.RolePermissionsData;
import org.mifosng.platform.api.infrastructure.PortfolioApiDataConversionService; import org.mifosng.platform.api.infrastructure.PortfolioApiDataConversionService;
import org.mifosng.platform.api.infrastructure.PortfolioApiJsonSerializerService; import org.mifosng.platform.api.infrastructure.PortfolioApiJsonSerializerService;
import org.mifosng.platform.infrastructure.api.ApiParameterHelper; import org.mifosng.platform.infrastructure.api.ApiParameterHelper;
import org.mifosng.platform.makerchecker.service.PortfolioCommandSourceWritePlatformService;
import org.mifosng.platform.security.PlatformSecurityContext; import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.service.PermissionReadPlatformService; import org.mifosng.platform.user.service.PermissionReadPlatformService;
import org.mifosng.platform.user.service.RoleReadPlatformService; import org.mifosng.platform.user.service.RoleReadPlatformService;
@@ -53,6 +54,9 @@ public class RolesApiResource {
@Autowired @Autowired
private PortfolioApiJsonSerializerService apiJsonSerializerService; private PortfolioApiJsonSerializerService apiJsonSerializerService;
@Autowired
private PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired @Autowired
private PlatformSecurityContext context; private PlatformSecurityContext context;
@@ -77,7 +81,7 @@ public class RolesApiResource {
@Path("template") @Path("template")
@Consumes({MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON})
public String retrieveNewRoleDetails(@Context final UriInfo uriInfo) { public String retrieveRoleTemplate(@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions); context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
@@ -94,13 +98,11 @@ public class RolesApiResource {
@POST @POST
@Consumes({MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON})
public String createRole(final String jsonRequestBody) { public String createRole(final String apiRequestBodyAsJson) {
final RoleCommand command = this.apiDataConversionService.convertJsonToRoleCommand(null, jsonRequestBody); final EntityIdentifier result = this.commandsSourceWritePlatformService.logCommandSource("CREATE", "roles", null, apiRequestBodyAsJson);
final Long roleId = this.roleWritePlatformService.createRole(command); return this.apiJsonSerializerService.serializeEntityIdentifier(result);
return this.apiJsonSerializerService.serializeEntityIdentifier(new EntityIdentifier(roleId));
} }
// TODO - General thing to fix about REST API is if use partial response // TODO - General thing to fix about REST API is if use partial response
@@ -140,7 +142,7 @@ public class RolesApiResource {
@Produces({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON})
public String updateRole(@PathParam("roleId") final Long roleId, final String jsonRequestBody) { public String updateRole(@PathParam("roleId") final Long roleId, final String jsonRequestBody) {
final RoleCommand command = this.apiDataConversionService.convertJsonToRoleCommand(roleId, jsonRequestBody); final RoleCommand command = this.apiDataConversionService.convertApiRequestJsonToRoleCommand(roleId, jsonRequestBody);
this.roleWritePlatformService.updateRole(command); this.roleWritePlatformService.updateRole(command);
@@ -21,15 +21,8 @@ public class ClientCommand {
private final transient boolean makerCheckerApproval; private final transient boolean makerCheckerApproval;
private final transient Set<String> parametersPassedInRequest; private final transient Set<String> parametersPassedInRequest;
public ClientCommand( public ClientCommand(final Set<String> modifiedParameters, final Long id, final String externalId, final String firstname,
final Set<String> modifiedParameters, final String lastname, final String clientOrBusinessName, final Long officeId, final LocalDate joiningDate,
final Long id,
final String externalId,
final String firstname,
final String lastname,
final String clientOrBusinessName,
final Long officeId,
final LocalDate joiningDate,
final boolean makerCheckerApproval) { final boolean makerCheckerApproval) {
this.parametersPassedInRequest = modifiedParameters; this.parametersPassedInRequest = modifiedParameters;
this.id = id; this.id = id;
@@ -4,17 +4,26 @@ import java.util.Set;
/** /**
* Immutable command for creating or updating details of a role. * Immutable command for creating or updating details of a role.
*
* <p>Fields that are transient are intended not to be serialized into JSON.</p>
*/ */
public class RoleCommand { public class RoleCommand {
private final Long id; private final transient Long id;
private final String name; private final String name;
private final String description; private final String description;
private final Set<String> modifiedParameters; private final transient boolean makerCheckerApproval;
private final transient Set<String> modifiedParameters;
public RoleCommand(Set<String> modifiedParameters, final Long id, final String name, final String description) { public RoleCommand(
final Set<String> modifiedParameters,
final boolean makerCheckerApproval,
final Long id,
final String name,
final String description) {
this.modifiedParameters = modifiedParameters; this.modifiedParameters = modifiedParameters;
this.makerCheckerApproval = makerCheckerApproval;
this.id = id; this.id = id;
this.name = name; this.name = name;
this.description = description; this.description = description;
@@ -39,4 +48,8 @@ public class RoleCommand {
public boolean isDescriptionChanged() { public boolean isDescriptionChanged() {
return this.modifiedParameters.contains("description"); return this.modifiedParameters.contains("description");
} }
public boolean isApprovedByChecker() {
return this.makerCheckerApproval;
}
} }
@@ -40,7 +40,7 @@ public interface PortfolioApiDataConversionService {
OfficeCommand convertJsonToOfficeCommand(Long resourceIdentifier, String json); OfficeCommand convertJsonToOfficeCommand(Long resourceIdentifier, String json);
RoleCommand convertJsonToRoleCommand(Long resourceIdentifier, String json); RoleCommand convertApiRequestJsonToRoleCommand(Long resourceIdentifier, String json);
RolePermissionCommand convertJsonToRolePermissionCommand(Long roleId, String jsonRequestBody); RolePermissionCommand convertJsonToRolePermissionCommand(Long roleId, String jsonRequestBody);
@@ -53,6 +53,7 @@ import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
import org.mifosng.platform.infrastructure.api.JsonParserHelper; import org.mifosng.platform.infrastructure.api.JsonParserHelper;
import org.mifosng.platform.infrastructure.errorhandling.InvalidJsonException; import org.mifosng.platform.infrastructure.errorhandling.InvalidJsonException;
import org.mifosng.platform.infrastructure.errorhandling.UnsupportedParameterException; import org.mifosng.platform.infrastructure.errorhandling.UnsupportedParameterException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.number.NumberFormatter; import org.springframework.format.number.NumberFormatter;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -66,9 +67,20 @@ import com.google.gson.reflect.TypeToken;
@Service @Service
public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataConversionService { public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataConversionService {
/**
* Used in places where the support json of the api request is not any
* different to that of the json of a serialized command object.
*/
private final PortfolioCommandDeserializerService portfolioCommandDeserializerService;
/**
* Google-gson class for converting to and from json.
*/
private final Gson gsonConverter; private final Gson gsonConverter;
public PortfolioApiDataConversionServiceImpl() { @Autowired
public PortfolioApiDataConversionServiceImpl(final PortfolioCommandDeserializerService portfolioCommandDeserializerService) {
this.portfolioCommandDeserializerService = portfolioCommandDeserializerService;
gsonConverter = new Gson(); gsonConverter = new Gson();
} }
@@ -229,22 +241,20 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo
} }
@Override @Override
public RoleCommand convertJsonToRoleCommand(final Long resourceIdentifier, final String json) { public RoleCommand convertApiRequestJsonToRoleCommand(final Long resourceIdentifier, final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType(); final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> requestMap = gsonConverter.fromJson(json, typeOfMap); final Map<String, Object> requestMap = gsonConverter.fromJson(json, typeOfMap);
Set<String> supportedParams = new HashSet<String>(Arrays.asList("name", "description", "permissions")); final Set<String> supportedParams = new HashSet<String>(Arrays.asList("id", "name", "description"));
checkForUnsupportedParameters(requestMap, supportedParams); checkForUnsupportedParameters(requestMap, supportedParams);
Set<String> modifiedParameters = new HashSet<String>(); // no difference between the api json and internal command
// representation of json so reuse this code to de-serialize into
String name = extractStringParameter("name", requestMap, modifiedParameters); // command object
String description = extractStringParameter("description", requestMap, modifiedParameters); return this.portfolioCommandDeserializerService.deserializeRoleCommand(resourceIdentifier, json, false);
return new RoleCommand(modifiedParameters, resourceIdentifier, name, description);
} }
@Override @Override
@@ -13,6 +13,7 @@ import org.mifosng.platform.api.data.ClientAccountSummaryCollectionData;
import org.mifosng.platform.api.data.ClientData; import org.mifosng.platform.api.data.ClientData;
import org.mifosng.platform.api.data.ClientIdentifierData; import org.mifosng.platform.api.data.ClientIdentifierData;
import org.mifosng.platform.api.data.CodeData; import org.mifosng.platform.api.data.CodeData;
import org.mifosng.platform.api.data.CommandSourceData;
import org.mifosng.platform.api.data.ConfigurationData; import org.mifosng.platform.api.data.ConfigurationData;
import org.mifosng.platform.api.data.DatatableData; import org.mifosng.platform.api.data.DatatableData;
import org.mifosng.platform.api.data.DepositAccountData; import org.mifosng.platform.api.data.DepositAccountData;
@@ -30,7 +31,6 @@ import org.mifosng.platform.api.data.LoanProductData;
import org.mifosng.platform.api.data.LoanReassignmentData; import org.mifosng.platform.api.data.LoanReassignmentData;
import org.mifosng.platform.api.data.LoanScheduleData; import org.mifosng.platform.api.data.LoanScheduleData;
import org.mifosng.platform.api.data.LoanTransactionData; import org.mifosng.platform.api.data.LoanTransactionData;
import org.mifosng.platform.api.data.CommandSourceData;
import org.mifosng.platform.api.data.NoteData; import org.mifosng.platform.api.data.NoteData;
import org.mifosng.platform.api.data.OfficeData; import org.mifosng.platform.api.data.OfficeData;
import org.mifosng.platform.api.data.OfficeTransactionData; import org.mifosng.platform.api.data.OfficeTransactionData;
@@ -157,9 +157,9 @@ public interface PortfolioApiJsonSerializerService {
String serializeSavingAccountsDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<SavingAccountData> accounts); String serializeSavingAccountsDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<SavingAccountData> accounts);
// TODO - KW - might devide serialize up into portfolio and account to reduce interface
String serializeChartOfAccountDataToJson(boolean prettyPrint, Set<String> responseParameters, ChartOfAccountsData chartOfAccounts);
String serializeGuarantorDataToJson(boolean prettyPrint, Set<String> responseParameters, GuarantorData guarantorData); String serializeGuarantorDataToJson(boolean prettyPrint, Set<String> responseParameters, GuarantorData guarantorData);
// TODO - KW - might devide serialize up into portfolio and account to
// reduce interface
String serializeChartOfAccountDataToJson(boolean prettyPrint, Set<String> responseParameters, ChartOfAccountsData chartOfAccounts);
} }
@@ -0,0 +1,42 @@
package org.mifosng.platform.api.infrastructure;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.mifosng.platform.api.commands.RoleCommand;
import org.mifosng.platform.infrastructure.api.JsonParserHelper;
import org.mifosng.platform.infrastructure.errorhandling.InvalidJsonException;
import org.springframework.stereotype.Service;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
/**
* Implementation of {@link PortfolioCommandDeserializerService} that de-serializes
* JSON of commands into Java object representation using google-gson.
*/
@Service
public class PortfolioCommandDeerializerServiceGoogleGson implements PortfolioCommandDeserializerService {
private final JsonParser parser;
public PortfolioCommandDeerializerServiceGoogleGson() {
parser = new JsonParser();
}
@Override
public RoleCommand deserializeRoleCommand(final Long roleId, final String commandAsJson, final boolean makerCheckerApproval) {
if (StringUtils.isBlank(commandAsJson)) { throw new InvalidJsonException(); }
final JsonParserHelper helper = new JsonParserHelper();
final JsonElement element = parser.parse(commandAsJson);
final Set<String> parametersPassedInRequest = new HashSet<String>();
final String name = helper.extractStringNamed("name", element, parametersPassedInRequest);
final String description = helper.extractStringNamed("description", element, parametersPassedInRequest);
return new RoleCommand(parametersPassedInRequest, makerCheckerApproval, roleId, name, description);
}
}
@@ -0,0 +1,18 @@
package org.mifosng.platform.api.infrastructure;
import org.mifosng.platform.api.commands.RoleCommand;
/**
* Service for de-serializing JSON for a command into the platforms internal
* Java object representation of the command.
*
* <p>
* Known implementations:
* </p>
*
* @see PortfolioCommandDeerializerServiceGoogleGson
*/
public interface PortfolioCommandDeserializerService {
RoleCommand deserializeRoleCommand(Long roleId, String commandAsJson, boolean makerCheckerApproval);
}
@@ -0,0 +1,14 @@
package org.mifosng.platform.api.infrastructure;
import org.mifosng.platform.api.commands.RoleCommand;
/**
* Service for serializing commands into another format.
*
* <p>Known implementations:</p>
* @see PortfolioCommandSerializerServiceJson
*/
public interface PortfolioCommandSerializerService {
String serializeRoleCommandToJson(RoleCommand command);
}
@@ -0,0 +1,32 @@
package org.mifosng.platform.api.infrastructure;
import java.util.HashSet;
import org.mifosng.platform.api.commands.RoleCommand;
import org.mifosng.platform.infrastructure.api.GoogleGsonSerializerHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.google.gson.Gson;
/**
* Implementation of {@link PortfolioCommandSerializerService} that serializes
* the commands into JSON using google-gson.
*/
@Service
public class PortfolioCommandSerializerServiceJson implements PortfolioCommandSerializerService {
private final GoogleGsonSerializerHelper helper;
@Autowired
public PortfolioCommandSerializerServiceJson(final GoogleGsonSerializerHelper helper) {
this.helper = helper;
}
@Override
public String serializeRoleCommandToJson(final RoleCommand command) {
final Gson gsonDeserializer = helper.createGsonBuilderWithParameterExclusionSerializationStrategy(new HashSet<String>(), false,
new HashSet<String>());
return helper.serializedJsonFrom(gsonDeserializer, command);
}
}
@@ -42,16 +42,13 @@ public class JsonParserHelper {
return value; return value;
} }
public Long extractLongNamed(final String parameterName, public Long extractLongNamed(final String parameterName, final JsonElement element, final Set<String> parametersPassedInRequest) {
final JsonElement element, final Set<String> parametersPassedInRequest) {
Long longValue = null; Long longValue = null;
if (element.isJsonObject()) { if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject(); JsonObject object = element.getAsJsonObject();
if (object.has(parameterName) if (object.has(parameterName) && object.get(parameterName).isJsonPrimitive()) {
&& object.get(parameterName).isJsonPrimitive()) {
parametersPassedInRequest.add(parameterName); parametersPassedInRequest.add(parameterName);
JsonPrimitive primitive = object.get(parameterName) JsonPrimitive primitive = object.get(parameterName).getAsJsonPrimitive();
.getAsJsonPrimitive();
final String stringValue = primitive.getAsString(); final String stringValue = primitive.getAsString();
if (StringUtils.isNotBlank(stringValue)) { if (StringUtils.isNotBlank(stringValue)) {
longValue = Long.valueOf(stringValue); longValue = Long.valueOf(stringValue);
@@ -77,10 +74,7 @@ public class JsonParserHelper {
return stringValue; return stringValue;
} }
public BigDecimal extractBigDecimalNamed( public BigDecimal extractBigDecimalNamed(final String parameterName, final JsonElement element, final Set<String> modifiedParameters) {
final String parameterName,
final JsonElement element,
final Set<String> modifiedParameters) {
BigDecimal value = null; BigDecimal value = null;
if (element.isJsonObject()) { if (element.isJsonObject()) {
final JsonObject object = element.getAsJsonObject(); final JsonObject object = element.getAsJsonObject();
@@ -90,19 +84,14 @@ public class JsonParserHelper {
return value; return value;
} }
public BigDecimal extractBigDecimalNamed( public BigDecimal extractBigDecimalNamed(final String parameterName, final JsonObject element, final Locale locale,
final String parameterName,
final JsonObject element,
final Locale locale,
final Set<String> modifiedParameters) { final Set<String> modifiedParameters) {
BigDecimal value = null; BigDecimal value = null;
if (element.isJsonObject()) { if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject(); JsonObject object = element.getAsJsonObject();
if (object.has(parameterName) if (object.has(parameterName) && object.get(parameterName).isJsonPrimitive()) {
&& object.get(parameterName).isJsonPrimitive()) {
modifiedParameters.add(parameterName); modifiedParameters.add(parameterName);
JsonPrimitive primitive = object.get(parameterName) JsonPrimitive primitive = object.get(parameterName).getAsJsonPrimitive();
.getAsJsonPrimitive();
String valueAsString = primitive.getAsString(); String valueAsString = primitive.getAsString();
if (StringUtils.isNotBlank(valueAsString)) { if (StringUtils.isNotBlank(valueAsString)) {
value = convertFrom(valueAsString, parameterName, locale); value = convertFrom(valueAsString, parameterName, locale);
@@ -112,10 +101,7 @@ public class JsonParserHelper {
return value; return value;
} }
public Integer extractIntegerNamed( public Integer extractIntegerNamed(final String parameterName, final JsonElement element, final Set<String> modifiedParameters) {
final String parameterName,
final JsonElement element,
final Set<String> modifiedParameters) {
Integer value = null; Integer value = null;
if (element.isJsonObject()) { if (element.isJsonObject()) {
final JsonObject object = element.getAsJsonObject(); final JsonObject object = element.getAsJsonObject();
@@ -125,18 +111,14 @@ public class JsonParserHelper {
return value; return value;
} }
public Integer extractIntegerNamed( public Integer extractIntegerNamed(final String parameterName, final JsonElement element, final Locale locale,
final String parameterName,
final JsonElement element,
final Locale locale,
final Set<String> modifiedParameters) { final Set<String> modifiedParameters) {
Integer value = null; Integer value = null;
if (element.isJsonObject()) { if (element.isJsonObject()) {
final JsonObject object = element.getAsJsonObject(); final JsonObject object = element.getAsJsonObject();
if (object.has(parameterName) && object.get(parameterName).isJsonPrimitive()) { if (object.has(parameterName) && object.get(parameterName).isJsonPrimitive()) {
modifiedParameters.add(parameterName); modifiedParameters.add(parameterName);
JsonPrimitive primitive = object.get(parameterName) JsonPrimitive primitive = object.get(parameterName).getAsJsonPrimitive();
.getAsJsonPrimitive();
String valueAsString = primitive.getAsString(); String valueAsString = primitive.getAsString();
if (StringUtils.isNotBlank(valueAsString)) { if (StringUtils.isNotBlank(valueAsString)) {
value = convertToInteger(valueAsString, parameterName, locale); value = convertToInteger(valueAsString, parameterName, locale);
@@ -152,8 +134,7 @@ public class JsonParserHelper {
JsonObject object = element.getAsJsonObject(); JsonObject object = element.getAsJsonObject();
final String dateFormatParameter = "dateFormat"; final String dateFormatParameter = "dateFormat";
if (object.has(dateFormatParameter) if (object.has(dateFormatParameter) && object.get(dateFormatParameter).isJsonPrimitive()) {
&& object.get(dateFormatParameter).isJsonPrimitive()) {
final JsonPrimitive primitive = object.get(dateFormatParameter).getAsJsonPrimitive(); final JsonPrimitive primitive = object.get(dateFormatParameter).getAsJsonPrimitive();
value = primitive.getAsString(); value = primitive.getAsString();
} }
@@ -168,8 +149,7 @@ public class JsonParserHelper {
String locale = null; String locale = null;
final String localeParameter = "locale"; final String localeParameter = "locale";
if (object.has(localeParameter) if (object.has(localeParameter) && object.get(localeParameter).isJsonPrimitive()) {
&& object.get(localeParameter).isJsonPrimitive()) {
final JsonPrimitive primitive = object.get(localeParameter).getAsJsonPrimitive(); final JsonPrimitive primitive = object.get(localeParameter).getAsJsonPrimitive();
locale = primitive.getAsString(); locale = primitive.getAsString();
clientApplicationLocale = localeFromString(locale); clientApplicationLocale = localeFromString(locale);
@@ -181,7 +161,8 @@ public class JsonParserHelper {
/** /**
* Used with the local date is in array format * Used with the local date is in array format
*/ */
public LocalDate extractLocalDateAsArrayNamed(final String parameterName, final JsonElement element, final Set<String> parametersPassedInCommand) { public LocalDate extractLocalDateAsArrayNamed(final String parameterName, final JsonElement element,
final Set<String> parametersPassedInCommand) {
LocalDate value = null; LocalDate value = null;
if (element.isJsonObject()) { if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject(); JsonObject object = element.getAsJsonObject();
@@ -190,7 +171,6 @@ public class JsonParserHelper {
parametersPassedInCommand.add(parameterName); parametersPassedInCommand.add(parameterName);
final JsonArray dateArray = object.get(parameterName).getAsJsonArray(); final JsonArray dateArray = object.get(parameterName).getAsJsonArray();
Integer year = dateArray.get(0).getAsInt(); Integer year = dateArray.get(0).getAsInt();
@@ -204,9 +184,7 @@ public class JsonParserHelper {
return value; return value;
} }
public LocalDate extractLocalDateNamed( public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element,
final String parameterName,
final JsonElement element,
final Set<String> parametersPassedInCommand) { final Set<String> parametersPassedInCommand) {
LocalDate value = null; LocalDate value = null;
@@ -221,18 +199,13 @@ public class JsonParserHelper {
return value; return value;
} }
public LocalDate extractLocalDateNamed( public LocalDate extractLocalDateNamed(final String parameterName, final JsonObject element, final String dateFormat,
final String parameterName, final Locale clientApplicationLocale, final Set<String> parametersPassedInCommand) {
final JsonObject element,
final String dateFormat,
final Locale clientApplicationLocale,
final Set<String> parametersPassedInCommand) {
LocalDate value = null; LocalDate value = null;
if (element.isJsonObject()) { if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject(); JsonObject object = element.getAsJsonObject();
if (object.has(parameterName) if (object.has(parameterName) && object.get(parameterName).isJsonPrimitive()) {
&& object.get(parameterName).isJsonPrimitive()) {
parametersPassedInCommand.add(parameterName); parametersPassedInCommand.add(parameterName);
@@ -247,88 +220,64 @@ public class JsonParserHelper {
return value; return value;
} }
public LocalDate convertFrom(final String dateAsString, public LocalDate convertFrom(final String dateAsString, final String parameterName, final String dateFormat,
final String parameterName, final String dateFormat,
final Locale clientApplicationLocale) { final Locale clientApplicationLocale) {
if (StringUtils.isBlank(dateFormat) || clientApplicationLocale == null) { if (StringUtils.isBlank(dateFormat) || clientApplicationLocale == null) {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
if (StringUtils.isBlank(dateFormat)) { if (StringUtils.isBlank(dateFormat)) {
String defaultMessage = new StringBuilder( String defaultMessage = new StringBuilder("The parameter '" + parameterName
"The parameter '" + "' requires a 'dateFormat' parameter to be passed with it.").toString();
+ parameterName ApiParameterError error = ApiParameterError.parameterError("validation.msg.missing.dateFormat.parameter", defaultMessage,
+ "' requires a 'dateFormat' parameter to be passed with it.") parameterName);
.toString();
ApiParameterError error = ApiParameterError.parameterError(
"validation.msg.missing.dateFormat.parameter",
defaultMessage, parameterName);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
if (clientApplicationLocale == null) { if (clientApplicationLocale == null) {
String defaultMessage = new StringBuilder( String defaultMessage = new StringBuilder("The parameter '" + parameterName
"The parameter '" + "' requires a 'locale' parameter to be passed with it.").toString();
+ parameterName ApiParameterError error = ApiParameterError.parameterError("validation.msg.missing.locale.parameter", defaultMessage,
+ "' requires a 'locale' parameter to be passed with it.") parameterName);
.toString();
ApiParameterError error = ApiParameterError.parameterError(
"validation.msg.missing.locale.parameter",
defaultMessage, parameterName);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
throw new PlatformApiDataValidationException( throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
"validation.msg.validation.errors.exist", dataValidationErrors);
"Validation errors exist.", dataValidationErrors);
} }
LocalDate eventLocalDate = null; LocalDate eventLocalDate = null;
if (StringUtils.isNotBlank(dateAsString)) { if (StringUtils.isNotBlank(dateAsString)) {
try { try {
// Locale locale = LocaleContextHolder.getLocale(); // Locale locale = LocaleContextHolder.getLocale();
eventLocalDate = DateTimeFormat eventLocalDate = DateTimeFormat.forPattern(dateFormat).withLocale(clientApplicationLocale)
.forPattern(dateFormat) .parseLocalDate(dateAsString.toLowerCase(clientApplicationLocale));
.withLocale(clientApplicationLocale)
.parseLocalDate(
dateAsString
.toLowerCase(clientApplicationLocale));
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
ApiParameterError error = ApiParameterError.parameterError( ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.date.format", "The parameter "
"validation.msg.invalid.date.format", "The parameter " + parameterName + " is invalid based on the dateFormat: '" + dateFormat + "' and locale: '"
+ parameterName + clientApplicationLocale + "' provided:", parameterName, dateAsString, dateFormat);
+ " is invalid based on the dateFormat: '"
+ dateFormat + "' and locale: '"
+ clientApplicationLocale + "' provided:",
parameterName, dateAsString, dateFormat);
dataValidationErrors.add(error); dataValidationErrors.add(error);
throw new PlatformApiDataValidationException( throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
"validation.msg.validation.errors.exist", dataValidationErrors);
"Validation errors exist.", dataValidationErrors);
} }
} }
return eventLocalDate; return eventLocalDate;
} }
public Integer convertToInteger(final String numericalValueFormatted, public Integer convertToInteger(final String numericalValueFormatted, final String parameterName, final Locale clientApplicationLocale) {
final String parameterName, final Locale clientApplicationLocale) {
if (clientApplicationLocale == null) { if (clientApplicationLocale == null) {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
String defaultMessage = new StringBuilder("The parameter '" String defaultMessage = new StringBuilder("The parameter '" + parameterName
+ parameterName + "' requires a 'locale' parameter to be passed with it.").toString();
+ "' requires a 'locale' parameter to be passed with it.") ApiParameterError error = ApiParameterError.parameterError("validation.msg.missing.locale.parameter", defaultMessage,
.toString();
ApiParameterError error = ApiParameterError.parameterError(
"validation.msg.missing.locale.parameter", defaultMessage,
parameterName); parameterName);
dataValidationErrors.add(error); dataValidationErrors.add(error);
throw new PlatformApiDataValidationException( throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
"validation.msg.validation.errors.exist", dataValidationErrors);
"Validation errors exist.", dataValidationErrors);
} }
try { try {
@@ -338,8 +287,7 @@ public class JsonParserHelper {
String source = numericalValueFormatted.trim(); String source = numericalValueFormatted.trim();
NumberFormat format = NumberFormat NumberFormat format = NumberFormat.getInstance(clientApplicationLocale);
.getInstance(clientApplicationLocale);
DecimalFormat df = (DecimalFormat) format; DecimalFormat df = (DecimalFormat) format;
DecimalFormatSymbols symbols = df.getDecimalFormatSymbols(); DecimalFormatSymbols symbols = df.getDecimalFormatSymbols();
df.setParseBigDecimal(true); df.setParseBigDecimal(true);
@@ -347,8 +295,7 @@ public class JsonParserHelper {
// http://bugs.sun.com/view_bug.do?bug_id=4510618 // http://bugs.sun.com/view_bug.do?bug_id=4510618
char groupingSeparator = symbols.getGroupingSeparator(); char groupingSeparator = symbols.getGroupingSeparator();
if (groupingSeparator == '\u00a0') { if (groupingSeparator == '\u00a0') {
source = source.replaceAll(" ", source = source.replaceAll(" ", Character.toString('\u00a0'));
Character.toString('\u00a0'));
} }
Number parsedNumber = df.parse(source); Number parsedNumber = df.parse(source);
@@ -356,15 +303,10 @@ public class JsonParserHelper {
double parsedNumberDouble = parsedNumber.doubleValue(); double parsedNumberDouble = parsedNumber.doubleValue();
int parsedNumberInteger = parsedNumber.intValue(); int parsedNumberInteger = parsedNumber.intValue();
if (source.contains(Character.toString(symbols if (source.contains(Character.toString(symbols.getDecimalSeparator()))) { throw new ParseException(source, 0); }
.getDecimalSeparator()))) {
throw new ParseException(source, 0);
}
if (!Double.valueOf(parsedNumberDouble).equals( if (!Double.valueOf(parsedNumberDouble).equals(Double.valueOf(Integer.valueOf(parsedNumberInteger)))) { throw new ParseException(
Double.valueOf(Integer.valueOf(parsedNumberInteger)))) { source, 0); }
throw new ParseException(source, 0);
}
number = parsedNumber.intValue(); number = parsedNumber.intValue();
} }
@@ -373,43 +315,29 @@ public class JsonParserHelper {
} catch (ParseException e) { } catch (ParseException e) {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
ApiParameterError error = ApiParameterError ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.integer.format", "The parameter "
.parameterError( + parameterName + " has value: " + numericalValueFormatted + " which is invalid integer value for provided locale of ["
"validation.msg.invalid.integer.format", + clientApplicationLocale.toString() + "].", parameterName, numericalValueFormatted, clientApplicationLocale);
"The parameter "
+ parameterName
+ " has value: "
+ numericalValueFormatted
+ " which is invalid integer value for provided locale of ["
+ clientApplicationLocale.toString() + "].",
parameterName, numericalValueFormatted,
clientApplicationLocale);
dataValidationErrors.add(error); dataValidationErrors.add(error);
throw new PlatformApiDataValidationException( throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
"validation.msg.validation.errors.exist", dataValidationErrors);
"Validation errors exist.", dataValidationErrors);
} }
} }
public BigDecimal convertFrom(final String numericalValueFormatted, public BigDecimal convertFrom(final String numericalValueFormatted, final String parameterName, final Locale clientApplicationLocale) {
final String parameterName, final Locale clientApplicationLocale) {
if (clientApplicationLocale == null) { if (clientApplicationLocale == null) {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
String defaultMessage = new StringBuilder("The parameter '" String defaultMessage = new StringBuilder("The parameter '" + parameterName
+ parameterName + "' requires a 'locale' parameter to be passed with it.").toString();
+ "' requires a 'locale' parameter to be passed with it.") ApiParameterError error = ApiParameterError.parameterError("validation.msg.missing.locale.parameter", defaultMessage,
.toString();
ApiParameterError error = ApiParameterError.parameterError(
"validation.msg.missing.locale.parameter", defaultMessage,
parameterName); parameterName);
dataValidationErrors.add(error); dataValidationErrors.add(error);
throw new PlatformApiDataValidationException( throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
"validation.msg.validation.errors.exist", dataValidationErrors);
"Validation errors exist.", dataValidationErrors);
} }
try { try {
@@ -419,44 +347,31 @@ public class JsonParserHelper {
String source = numericalValueFormatted.trim(); String source = numericalValueFormatted.trim();
NumberFormat format = NumberFormat NumberFormat format = NumberFormat.getNumberInstance(clientApplicationLocale);
.getNumberInstance(clientApplicationLocale);
DecimalFormat df = (DecimalFormat) format; DecimalFormat df = (DecimalFormat) format;
DecimalFormatSymbols symbols = df.getDecimalFormatSymbols(); DecimalFormatSymbols symbols = df.getDecimalFormatSymbols();
// http://bugs.sun.com/view_bug.do?bug_id=4510618 // http://bugs.sun.com/view_bug.do?bug_id=4510618
char groupingSeparator = symbols.getGroupingSeparator(); char groupingSeparator = symbols.getGroupingSeparator();
if (groupingSeparator == '\u00a0') { if (groupingSeparator == '\u00a0') {
source = source.replaceAll(" ", source = source.replaceAll(" ", Character.toString('\u00a0'));
Character.toString('\u00a0'));
} }
NumberFormatter numberFormatter = new NumberFormatter(); NumberFormatter numberFormatter = new NumberFormatter();
Number parsedNumber = numberFormatter.parse(source, Number parsedNumber = numberFormatter.parse(source, clientApplicationLocale);
clientApplicationLocale); number = BigDecimal.valueOf(Double.valueOf(parsedNumber.doubleValue()));
number = BigDecimal.valueOf(Double.valueOf(parsedNumber
.doubleValue()));
} }
return number; return number;
} catch (ParseException e) { } catch (ParseException e) {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
ApiParameterError error = ApiParameterError ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.decimal.format", "The parameter "
.parameterError( + parameterName + " has value: " + numericalValueFormatted + " which is invalid decimal value for provided locale of ["
"validation.msg.invalid.decimal.format", + clientApplicationLocale.toString() + "].", parameterName, numericalValueFormatted, clientApplicationLocale);
"The parameter "
+ parameterName
+ " has value: "
+ numericalValueFormatted
+ " which is invalid decimal value for provided locale of ["
+ clientApplicationLocale.toString() + "].",
parameterName, numericalValueFormatted,
clientApplicationLocale);
dataValidationErrors.add(error); dataValidationErrors.add(error);
throw new PlatformApiDataValidationException( throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
"validation.msg.validation.errors.exist", dataValidationErrors);
"Validation errors exist.", dataValidationErrors);
} }
} }
@@ -464,15 +379,12 @@ public class JsonParserHelper {
if (StringUtils.isBlank(localeAsString)) { if (StringUtils.isBlank(localeAsString)) {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
ApiParameterError error = ApiParameterError.parameterError( ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.locale.format",
"validation.msg.invalid.locale.format", "The parameter locale is invalid. It cannot be blank.", "locale");
"The parameter locale is invalid. It cannot be blank.",
"locale");
dataValidationErrors.add(error); dataValidationErrors.add(error);
throw new PlatformApiDataValidationException( throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
"validation.msg.validation.errors.exist", dataValidationErrors);
"Validation errors exist.", dataValidationErrors);
} }
String languageCode = ""; String languageCode = "";
@@ -499,40 +411,30 @@ public class JsonParserHelper {
return localeFrom(languageCode, courntryCode, variantCode); return localeFrom(languageCode, courntryCode, variantCode);
} }
private Locale localeFrom(final String languageCode, private Locale localeFrom(final String languageCode, final String courntryCode, final String variantCode) {
final String courntryCode, final String variantCode) {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
List<String> allowedLanguages = Arrays.asList(Locale.getISOLanguages()); List<String> allowedLanguages = Arrays.asList(Locale.getISOLanguages());
if (!allowedLanguages.contains(languageCode.toLowerCase())) { if (!allowedLanguages.contains(languageCode.toLowerCase())) {
ApiParameterError error = ApiParameterError.parameterError( ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.locale.format",
"validation.msg.invalid.locale.format", "The parameter locale has an invalid language value " + languageCode + " .", "locale", languageCode);
"The parameter locale has an invalid language value "
+ languageCode + " .", "locale", languageCode);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
if (StringUtils.isNotBlank(courntryCode.toUpperCase())) { if (StringUtils.isNotBlank(courntryCode.toUpperCase())) {
List<String> allowedCountries = Arrays.asList(Locale List<String> allowedCountries = Arrays.asList(Locale.getISOCountries());
.getISOCountries());
if (!allowedCountries.contains(courntryCode)) { if (!allowedCountries.contains(courntryCode)) {
ApiParameterError error = ApiParameterError.parameterError( ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.locale.format",
"validation.msg.invalid.locale.format", "The parameter locale has an invalid country value " + courntryCode + " .", "locale", courntryCode);
"The parameter locale has an invalid country value "
+ courntryCode + " .", "locale", courntryCode);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
} }
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
throw new PlatformApiDataValidationException( "Validation errors exist.", dataValidationErrors); }
"validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors);
}
return new Locale(languageCode.toLowerCase(), return new Locale(languageCode.toLowerCase(), courntryCode.toUpperCase(), variantCode);
courntryCode.toUpperCase(), variantCode);
} }
private Locale extractLocaleValue(final JsonObject object) { private Locale extractLocaleValue(final JsonObject object) {
@@ -108,10 +108,6 @@ public class CommandSource extends AbstractPersistable<Long> {
return this.apiOperation; return this.apiOperation;
} }
public boolean isClientResource() {
return this.resource.equalsIgnoreCase("CLIENTS");
}
public boolean isCreate() { public boolean isCreate() {
return this.apiOperation.equalsIgnoreCase("CREATE"); return this.apiOperation.equalsIgnoreCase("CREATE");
} }
@@ -123,4 +119,12 @@ public class CommandSource extends AbstractPersistable<Long> {
public boolean isDelete() { public boolean isDelete() {
return this.apiOperation.equalsIgnoreCase("DELETE") && this.resourceId != null; return this.apiOperation.equalsIgnoreCase("DELETE") && this.resourceId != null;
} }
public boolean isClientResource() {
return this.resource.equalsIgnoreCase("CLIENTS");
}
public boolean isRoleResource() {
return this.resource.equalsIgnoreCase("ROLES");
}
} }
@@ -14,11 +14,15 @@ public class CommandSourceHandlerDelegator {
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final ClientCommandHandler clientCommandHandler; private final ClientCommandHandler clientCommandHandler;
private final RoleCommandHandler roleCommandHandler;
@Autowired @Autowired
public CommandSourceHandlerDelegator(final PlatformSecurityContext context, final ClientCommandHandler clientCommandHandler) { public CommandSourceHandlerDelegator(final PlatformSecurityContext context,
final ClientCommandHandler clientCommandHandler,
final RoleCommandHandler roleCommandHandler) {
this.context = context; this.context = context;
this.clientCommandHandler = clientCommandHandler; this.clientCommandHandler = clientCommandHandler;
this.roleCommandHandler = roleCommandHandler;
} }
public CommandSource handle(final CommandSource commandSource, final String apiRequestBodyInJson) { public CommandSource handle(final CommandSource commandSource, final String apiRequestBodyInJson) {
@@ -27,6 +31,8 @@ public class CommandSourceHandlerDelegator {
CommandSource commandSourceResult = null; CommandSource commandSourceResult = null;
if (commandSource.isClientResource()) { if (commandSource.isClientResource()) {
commandSourceResult = clientCommandHandler.handle(commandSource, apiRequestBodyInJson); commandSourceResult = clientCommandHandler.handle(commandSource, apiRequestBodyInJson);
} else if (commandSource.isRoleResource()) {
commandSourceResult = roleCommandHandler.handle(commandSource, apiRequestBodyInJson);
} else { } else {
throw new UnsupportedCommandException(commandSource.commandName()); throw new UnsupportedCommandException(commandSource.commandName());
} }
@@ -39,6 +45,8 @@ public class CommandSourceHandlerDelegator {
CommandSource commandSourceResult = null; CommandSource commandSourceResult = null;
if (commandSource.isClientResource()) { if (commandSource.isClientResource()) {
commandSourceResult = clientCommandHandler.handle(commandSource); commandSourceResult = clientCommandHandler.handle(commandSource);
} else if (commandSource.isRoleResource()) {
commandSourceResult = roleCommandHandler.handle(commandSource);
} else { } else {
throw new UnsupportedCommandException(commandSource.commandName()); throw new UnsupportedCommandException(commandSource.commandName());
} }
@@ -0,0 +1,110 @@
package org.mifosng.platform.makerchecker.service;
import org.joda.time.LocalDate;
import org.mifosng.platform.api.commands.RoleCommand;
import org.mifosng.platform.api.infrastructure.PortfolioApiDataConversionService;
import org.mifosng.platform.api.infrastructure.PortfolioCommandDeserializerService;
import org.mifosng.platform.api.infrastructure.PortfolioCommandSerializerService;
import org.mifosng.platform.client.service.RollbackTransactionAsCommandIsNotApprovedByCheckerException;
import org.mifosng.platform.infrastructure.errorhandling.UnsupportedCommandException;
import org.mifosng.platform.makerchecker.domain.CommandSource;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.domain.AppUser;
import org.mifosng.platform.user.service.RoleWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class RoleCommandHandler implements CommandSourceHandler {
private final PlatformSecurityContext context;
private final ChangeDetectionService changeDetectionService;
private final PortfolioApiDataConversionService apiDataConversionService;
private final PortfolioCommandSerializerService commandSerializerService;
private final PortfolioCommandDeserializerService commandDeserializerService;
private final RoleWritePlatformService writePlatformService;
@Autowired
public RoleCommandHandler(final PlatformSecurityContext context, final ChangeDetectionService changeDetectionService,
final PortfolioApiDataConversionService apiDataConversionService,
final PortfolioCommandSerializerService commandSerializerService,
final PortfolioCommandDeserializerService commandDeserializerService,
final RoleWritePlatformService writePlatformService) {
this.context = context;
this.changeDetectionService = changeDetectionService;
this.apiDataConversionService = apiDataConversionService;
this.commandSerializerService = commandSerializerService;
this.commandDeserializerService = commandDeserializerService;
this.writePlatformService = writePlatformService;
}
/*
* Used when users with 'create' capability create a command. If 'maker-checker' is not
* enabled for this specific command then the 'creator' is also marked 'as the checker' and command
* automatically is processed and changes state of system.
*/
public CommandSource handle(final CommandSource commandSource, final String apiRequestBodyInJson) {
final AppUser maker = context.authenticatedUser();
final LocalDate asToday = new LocalDate();
CommandSource commandSourceResult = commandSource.copy();
final Long resourceId = commandSource.resourceId();
Long newResourceId = null;
final RoleCommand command = this.apiDataConversionService.convertApiRequestJsonToRoleCommand(resourceId, apiRequestBodyInJson);
final String commandSerializedAsJson = this.commandSerializerService.serializeRoleCommandToJson(command);
commandSourceResult.updateJsonTo(commandSerializedAsJson);
if (commandSource.isCreate()) {
try {
newResourceId = this.writePlatformService.createRole(command);
commandSourceResult.markAsChecked(maker, asToday);
commandSourceResult.updateResourceId(newResourceId);
} catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) {
// swallow this rollback transaction by design
}
} else if (commandSource.isUpdate()) {
try {
final String jsonOfChangesOnly = this.changeDetectionService.detectChangesOnUpdate(commandSource.resourceName(), commandSource.resourceId(), commandSerializedAsJson);
commandSourceResult.updateJsonTo(jsonOfChangesOnly);
final RoleCommand changesOnly = this.commandDeserializerService.deserializeRoleCommand(resourceId, jsonOfChangesOnly, false);
newResourceId = this.writePlatformService.updateRole(changesOnly);
commandSourceResult.markAsChecked(maker, asToday);
} catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) {
// swallow this rollback transaction by design
}
} else if (commandSource.isDelete()) {
throw new UnsupportedCommandException(commandSource.commandName());
}
return commandSourceResult;
}
/*
* Used when users with 'checker' capability approve a command.
*/
public CommandSource handle(final CommandSource commandSourceResult) {
Long resourceId = null;
if (commandSourceResult.isRoleResource()) {
final RoleCommand command = this.commandDeserializerService.deserializeRoleCommand(resourceId, commandSourceResult.json(), true);
if (commandSourceResult.isCreate()) {
resourceId = this.writePlatformService.createRole(command);
} else if (commandSourceResult.isUpdate()) {
resourceId = this.writePlatformService.updateRole(command);
} else if (commandSourceResult.isDelete()) {
throw new UnsupportedCommandException(commandSourceResult.commandName());
}
}
commandSourceResult.updateResourceId(resourceId);
return commandSourceResult;
}
}
@@ -32,7 +32,6 @@ public class Permission extends AbstractPersistable<Long> {
@Column(name = "action_name", nullable = true, length = 100) @Column(name = "action_name", nullable = true, length = 100)
private final String actionName; private final String actionName;
protected Permission() { protected Permission() {
this.grouping = null; this.grouping = null;
this.code = null; this.code = null;
@@ -42,7 +41,7 @@ public class Permission extends AbstractPersistable<Long> {
this.actionName = null; this.actionName = null;
} }
public boolean hasCode(String checkCode) { public boolean hasCode(final String checkCode) {
return this.code.equalsIgnoreCase(checkCode); return this.code.equalsIgnoreCase(checkCode);
} }
@@ -5,6 +5,7 @@ import java.util.Map;
import org.mifosng.platform.accounting.api.commands.RolePermissionCommand; import org.mifosng.platform.accounting.api.commands.RolePermissionCommand;
import org.mifosng.platform.api.commands.RoleCommand; import org.mifosng.platform.api.commands.RoleCommand;
import org.mifosng.platform.client.service.RollbackTransactionAsCommandIsNotApprovedByCheckerException;
import org.mifosng.platform.exceptions.PermissionNotFoundException; import org.mifosng.platform.exceptions.PermissionNotFoundException;
import org.mifosng.platform.exceptions.RoleNotFoundException; import org.mifosng.platform.exceptions.RoleNotFoundException;
import org.mifosng.platform.security.PlatformSecurityContext; import org.mifosng.platform.security.PlatformSecurityContext;
@@ -17,18 +18,18 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@Service @Service
public class RoleWritePlatformServiceJpaRepositoryImpl implements public class RoleWritePlatformServiceJpaRepositoryImpl implements RoleWritePlatformService {
RoleWritePlatformService {
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final RoleRepository roleRepository; private final RoleRepository roleRepository;
private final PermissionRepository permissionRepository; private final PermissionRepository permissionRepository;
// FIXME - kw - this value is hardcoded for now but will be retrieved from
// configuration
private boolean makerCheckerEnabledForTask = false;
@Autowired @Autowired
public RoleWritePlatformServiceJpaRepositoryImpl( public RoleWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final RoleRepository roleRepository,
final PlatformSecurityContext context,
final RoleRepository roleRepository,
final PermissionRepository permissionRepository) { final PermissionRepository permissionRepository) {
this.context = context; this.context = context;
this.roleRepository = roleRepository; this.roleRepository = roleRepository;
@@ -48,6 +49,8 @@ public class RoleWritePlatformServiceJpaRepositoryImpl implements
this.roleRepository.save(entity); this.roleRepository.save(entity);
if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
return entity.getId(); return entity.getId();
} }
@@ -61,13 +64,13 @@ public class RoleWritePlatformServiceJpaRepositoryImpl implements
validator.validateForUpdate(); validator.validateForUpdate();
Role role = this.roleRepository.findOne(command.getId()); Role role = this.roleRepository.findOne(command.getId());
if (role == null) { if (role == null) { throw new RoleNotFoundException(command.getId()); }
throw new RoleNotFoundException(command.getId());
}
role.update(command); role.update(command);
this.roleRepository.save(role); this.roleRepository.save(role);
if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
return role.getId(); return role.getId();
} }
@@ -77,30 +80,24 @@ public class RoleWritePlatformServiceJpaRepositoryImpl implements
context.authenticatedUser(); context.authenticatedUser();
final Role role = this.roleRepository.findOne(command.getRoleId()); final Role role = this.roleRepository.findOne(command.getRoleId());
if (role == null) { if (role == null) { throw new RoleNotFoundException(command.getRoleId()); }
throw new RoleNotFoundException(command.getRoleId());
}
Collection<Permission> allPermissions = this.permissionRepository Collection<Permission> allPermissions = this.permissionRepository.findAll();
.findAll();
Map<String, Boolean> commandPermissions = command.getPermissions(); Map<String, Boolean> commandPermissions = command.getPermissions();
for (String permissionCode : commandPermissions.keySet()) { for (String permissionCode : commandPermissions.keySet()) {
Boolean selected = commandPermissions.get(permissionCode); Boolean selected = commandPermissions.get(permissionCode);
Permission permission = getPermissionByCode(allPermissions, final Permission permission = findPermissionByCode(allPermissions, permissionCode);
permissionCode);
if (role.getPermissions().contains(permission)) { if (role.getPermissions().contains(permission)) {
if (!(selected)) { if (!(selected)) {
role.getPermissions().remove(permission); role.getPermissions().remove(permission);
} }
} else { } else {
if (selected) { if (selected) {
role.getPermissions().add(permission); role.getPermissions().add(permission);
} }
} }
} }
@@ -109,13 +106,11 @@ public class RoleWritePlatformServiceJpaRepositoryImpl implements
return role.getId(); return role.getId();
} }
private Permission getPermissionByCode( private Permission findPermissionByCode(Collection<Permission> allPermissions, String permissionCode) {
Collection<Permission> allPermissions, String permissionCode) {
if (allPermissions != null) { if (allPermissions != null) {
for (Permission permission : allPermissions) { for (Permission permission : allPermissions) {
if (permission.code().equals(permissionCode)) return permission; if (permission.hasCode(permissionCode)) {return permission;}
} }
} }