MIFOSX-97: wip on supporting maker checker approach in platform starting with clients flow.

This commit is contained in:
Keith Woodlock
2012-11-19 21:13:28 +00:00
parent 30d4ca7681
commit f79da8144b
17 changed files with 396 additions and 224 deletions
@@ -24,7 +24,6 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang.StringUtils;
import org.mifosng.platform.api.commands.ClientCommand;
import org.mifosng.platform.api.commands.NoteCommand;
import org.mifosng.platform.api.data.ClientAccountSummaryCollectionData;
import org.mifosng.platform.api.data.ClientData;
@@ -159,7 +158,7 @@ public class ClientsApiResource {
@Path("{clientId}")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public String retrieveClientData(
public String retrieveClient(
@PathParam("clientId") final Long clientId,
@Context final UriInfo uriInfo) {
@@ -176,15 +175,46 @@ public class ClientsApiResource {
clientData = ClientData.templateOnTop(clientData, allowedOffices);
}
if (makerCheckerId != null) {
final CommandSourceData entry = this.commandSourceReadPlatformService.retrieveById(makerCheckerId);
final ClientCommand command = this.apiDataConversionService.convertJsonToClientCommand(null, entry.json(), false);
clientData = ClientData.integrateChanges(clientData, command);
clientData = handleMakerCheckerRequest(clientId, makerCheckerId, clientData);
}
boolean retrieveChanges = true;
if (retrieveChanges) {
final Collection<ClientData> dataChanges = retrieveAllUnprocessedDataChanges(clientId);
clientData = ClientData.integrateChanges(clientData, clientData.currentChange(), dataChanges);
}
return this.apiJsonSerializerService.serializeClientDataToJson(prettyPrint, responseParameters, clientData);
}
private ClientData handleMakerCheckerRequest(final Long clientId, final Long makerCheckerId, ClientData clientData) {
final CommandSourceData entry = this.commandSourceReadPlatformService.retrieveById(makerCheckerId);
final ClientData currentChange = this.apiDataConversionService.convertInternalJsonFormatToClientDataChange(clientId, entry.json());
final Collection<ClientData> dataChanges = retrieveAllUnprocessedDataChanges(clientId);
clientData = ClientData.integrateChanges(clientData, currentChange, dataChanges);
return clientData;
}
private Collection<ClientData> retrieveAllUnprocessedDataChanges(final Long clientId) {
Collection<ClientData> dataChanges = new ArrayList<ClientData>();
boolean retrieveChanges = true;
if (retrieveChanges) {
Collection<CommandSourceData> unprocessedChanges = this.commandSourceReadPlatformService.retrieveUnprocessChangesByResourceId("clients", clientId);
for (CommandSourceData commandSourceData : unprocessedChanges) {
ClientData change = this.apiDataConversionService.convertInternalJsonFormatToClientDataChange(clientId, commandSourceData.json());
dataChanges.add(change);
}
}
if (dataChanges.isEmpty()) {
dataChanges = null;
}
return dataChanges;
}
@GET
@Path("template")
@Consumes({MediaType.APPLICATION_JSON})
@@ -199,10 +229,7 @@ public class ClientsApiResource {
ClientData clientData = this.clientReadPlatformService.retrieveNewClientDetails();
if (makerCheckerId != null) {
final CommandSourceData entry = this.commandSourceReadPlatformService.retrieveById(makerCheckerId);
final ClientCommand command = this.apiDataConversionService.convertJsonToClientCommand(null, entry.json(), false);
clientData = ClientData.integrateChanges(clientData, command);
clientData = handleMakerCheckerRequest(null, makerCheckerId, clientData);
}
return this.apiJsonSerializerService.serializeClientDataToJson(prettyPrint, responseParameters, clientData);
@@ -9,7 +9,6 @@ import org.joda.time.LocalDate;
*/
public class ClientCommand {
private final Long id;
private final String externalId;
private final String firstname;
private final String lastname;
@@ -18,6 +17,7 @@ public class ClientCommand {
private final LocalDate joiningDate;
// made transient as dont want them to be serialized/converted to JSON
private final transient Long id;
private final transient boolean makerCheckerApproval;
private final transient Set<String> parametersPassedInRequest;
@@ -1,10 +1,10 @@
package org.mifosng.platform.api.data;
import java.util.Collection;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosng.platform.api.commands.ClientCommand;
/**
* Immutable data object representing client data.
@@ -22,13 +22,13 @@ final public class ClientData {
private final LocalDate joinedDate;
private final String imageKey;
@SuppressWarnings("unused")
private final boolean imagePresent;
private final Boolean imagePresent;
private final List<OfficeLookup> allowedOffices;
@SuppressWarnings("unused")
private final ClientCommand changes;
private final ClientData currentChange;
private final Collection<ClientData> allChanges;
private static String buildDisplayNameFrom(final String firstname, final String lastname) {
String displayName = null;
StringBuilder displayNameBuilder = new StringBuilder();
@@ -43,7 +43,27 @@ final public class ClientData {
return displayName;
}
public static ClientData integrateChanges(final ClientData clientData, final ClientCommand command) {
public static ClientData dataChangeInstance(
final Long id,
final Long officeId,
final String externalId,
final String firstname,
final String lastname,
final String clientOrBusinessName,
final LocalDate joiningDate) {
String firstnameValue = firstname;
String lastnameValue = lastname;
if (StringUtils.isNotBlank(clientOrBusinessName)) {
firstnameValue = null;
lastnameValue = clientOrBusinessName;
}
final String displayName = buildDisplayNameFrom(firstnameValue, lastnameValue);
return new ClientData(officeId, null, id, firstname, lastname, displayName, externalId, joiningDate, null, null, null, null);
}
public static ClientData integrateChanges(final ClientData clientData, ClientData currentChange, final Collection<ClientData> allChanges) {
String firstname = clientData.firstname;
String lastname = clientData.lastname;
if (StringUtils.isNotBlank(clientData.clientOrBusinessName)) {
@@ -52,11 +72,11 @@ final public class ClientData {
}
final String displayName = buildDisplayNameFrom(firstname, lastname);
return new ClientData(clientData.officeId, clientData.officeName, clientData.id, firstname, lastname, displayName,
clientData.externalId, clientData.joinedDate, clientData.imageKey, clientData.allowedOffices, command);
clientData.externalId, clientData.joinedDate, clientData.imageKey, clientData.allowedOffices, currentChange, allChanges);
}
public static ClientData template(final Long officeId, final LocalDate joinedDate, final List<OfficeLookup> allowedOffices) {
return new ClientData(officeId, null, null, null, null, null, null, joinedDate, null, allowedOffices, null);
return new ClientData(officeId, null, null, null, null, null, null, joinedDate, null, allowedOffices, null, null);
}
public static ClientData templateOnTop(final ClientData clientData, final List<OfficeLookup> allowedOffices) {
@@ -69,7 +89,7 @@ final public class ClientData {
}
final String displayName = buildDisplayNameFrom(firstname, lastname);
return new ClientData(clientData.officeId, clientData.officeName, clientData.id, firstname, lastname, displayName,
clientData.externalId, clientData.joinedDate, clientData.imageKey, allowedOffices, null);
clientData.externalId, clientData.joinedDate, clientData.imageKey, allowedOffices, clientData.currentChange, clientData.allChanges);
}
public static ClientData clientIdentifier(
@@ -81,7 +101,7 @@ final public class ClientData {
final String displayName = buildDisplayNameFrom(firstname, lastname);
return new ClientData(officeId, officeName, id, firstname, lastname, displayName, null, null, null, null, null);
return new ClientData(officeId, officeName, id, firstname, lastname, displayName, null, null, null, null, null, null);
}
public ClientData(
@@ -95,7 +115,8 @@ final public class ClientData {
final LocalDate joinedDate,
final String imageKey,
final List<OfficeLookup> allowedOffices,
final ClientCommand changes) {
final ClientData currentChange,
final Collection<ClientData> allChanges) {
this.officeId = officeId;
this.officeName = officeName;
this.id = id;
@@ -113,9 +134,14 @@ final public class ClientData {
this.externalId = externalId;
this.joinedDate = joinedDate;
this.imageKey = imageKey;
this.imagePresent = (imageKey != null);
if (imageKey != null) {
this.imagePresent = Boolean.TRUE;
} else {
this.imagePresent = null;
}
this.allowedOffices = allowedOffices;
this.changes = changes;
this.currentChange = currentChange;
this.allChanges = allChanges;
}
public String displayName() {
@@ -145,4 +171,8 @@ final public class ClientData {
public Long officeId() {
return this.officeId;
}
public ClientData currentChange() {
return this.currentChange;
}
}
@@ -7,6 +7,7 @@ import java.util.Set;
import org.mifosng.platform.accounting.api.data.ChartOfAccountsData;
import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.api.commands.ClientCommand;
import org.mifosng.platform.api.data.AdditionalFieldsSetData;
import org.mifosng.platform.api.data.AppUserData;
import org.mifosng.platform.api.data.AuthenticatedUserData;
@@ -497,14 +498,15 @@ public class GoogleGsonPortfolioApiJsonSerializerService implements PortfolioApi
}
@Override
public String serializeClientDataToJson(final boolean prettyPrint,
final Set<String> responseParameters,
final Collection<ClientData> clients) {
public String serializeClientCommandToJson(final ClientCommand command) {
final Gson gsonDeserializer = helper
.createGsonBuilderWithParameterExclusionSerializationStrategy(
CLIENT_DATA_PARAMETERS, prettyPrint, responseParameters);
return helper.serializedJsonFrom(gsonDeserializer,
clients.toArray(new ClientData[clients.size()]));
.createGsonBuilderWithParameterExclusionSerializationStrategy(new HashSet<String>(), false, new HashSet<String>());
return helper.serializedJsonFrom(gsonDeserializer, command);
}
@Override
public String serializeClientDataToJson(final ClientData client) {
return serializeClientDataToJson(false, new HashSet<String>(), client);
}
@Override
@@ -515,6 +517,17 @@ public class GoogleGsonPortfolioApiJsonSerializerService implements PortfolioApi
CLIENT_DATA_PARAMETERS, prettyPrint, responseParameters);
return helper.serializedJsonFrom(gsonDeserializer, client);
}
@Override
public String serializeClientDataToJson(final boolean prettyPrint,
final Set<String> responseParameters,
final Collection<ClientData> clients) {
final Gson gsonDeserializer = helper
.createGsonBuilderWithParameterExclusionSerializationStrategy(
CLIENT_DATA_PARAMETERS, prettyPrint, responseParameters);
return helper.serializedJsonFrom(gsonDeserializer,
clients.toArray(new ClientData[clients.size()]));
}
@Override
public String serializeClientAccountSummaryCollectionDataToJson(
@@ -28,6 +28,7 @@ import org.mifosng.platform.api.commands.SavingAccountCommand;
import org.mifosng.platform.api.commands.SavingProductCommand;
import org.mifosng.platform.api.commands.StaffCommand;
import org.mifosng.platform.api.commands.UserCommand;
import org.mifosng.platform.api.data.ClientData;
public interface PortfolioApiDataConversionService {
@@ -49,7 +50,13 @@ public interface PortfolioApiDataConversionService {
DepositProductCommand convertJsonToDepositProductCommand(Long resourceIdentifier, String json);
ClientCommand convertJsonToClientCommand(Long resourceIdentifier, String json, boolean makerCheckerApproval);
ClientCommand convertApiRequestJsonToClientCommand(Long resourceIdentifier, String json);
ClientCommand convertInternalJsonFormatToClientCommand(Long resourceIdentifier, String json, boolean checkerApproved);
ClientData convertInternalJsonFormatToClientDataChange(Long clientId, String json);
ClientCommand detectChanges(Long resourceId, String baseJson, String workingJson);
GroupCommand convertJsonToGroupCommand(Long resourceIdentifier, String json);
@@ -19,7 +19,6 @@ import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.mifosng.platform.api.commands.AdjustLoanTransactionCommand;
import org.mifosng.platform.api.commands.BranchMoneyTransferCommand;
import org.mifosng.platform.api.commands.LoanReassignmentCommand;
import org.mifosng.platform.api.commands.ChargeCommand;
import org.mifosng.platform.api.commands.ClientCommand;
import org.mifosng.platform.api.commands.ClientIdentifierCommand;
@@ -35,6 +34,7 @@ import org.mifosng.platform.api.commands.GroupCommand;
import org.mifosng.platform.api.commands.LoanApplicationCommand;
import org.mifosng.platform.api.commands.LoanChargeCommand;
import org.mifosng.platform.api.commands.LoanProductCommand;
import org.mifosng.platform.api.commands.LoanReassignmentCommand;
import org.mifosng.platform.api.commands.LoanStateTransitionCommand;
import org.mifosng.platform.api.commands.LoanTransactionCommand;
import org.mifosng.platform.api.commands.NoteCommand;
@@ -46,6 +46,7 @@ import org.mifosng.platform.api.commands.SavingProductCommand;
import org.mifosng.platform.api.commands.StaffCommand;
import org.mifosng.platform.api.commands.UserCommand;
import org.mifosng.platform.api.data.ApiParameterError;
import org.mifosng.platform.api.data.ClientData;
import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
import org.mifosng.platform.infrastructure.api.JsonParserHelper;
import org.mifosng.platform.infrastructure.errorhandling.InvalidJsonException;
@@ -376,7 +377,96 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo
}
@Override
public ClientCommand convertJsonToClientCommand(final Long resourceIdentifier, final String json, final boolean makerCheckerApproval) {
public ClientCommand detectChanges(final Long resourceIdentifier, final String baseJson, final String workingJson) {
final Type typeOfMap = new TypeToken<Map<String, Object>>(){}.getType();
final Map<String, Object> workingVersion = gsonConverter.fromJson(workingJson, typeOfMap);
final Set<String> workingParameters = workingVersion.keySet();
final JsonParser parser = new JsonParser();
final JsonElement baseElement = parser.parse(baseJson);
final JsonElement workingElement = parser.parse(workingJson);
final JsonParserHelper helper = new JsonParserHelper();
final Set<String> ignoreParameters = new HashSet<String>();
final Set<String> parametersPassedInRequest = new HashSet<String>();
Long officeId = null;
String externalId = null;
String firstname = null;
String lastname = null;
String clientOrBusinessName = null;
LocalDate joiningDate = null;
for (final String parameter : workingParameters) {
final String officeIdParameterName = "officeId";
if (officeIdParameterName.equalsIgnoreCase(parameter)) {
final Long baseOfficeId = helper.extractLongNamed(officeIdParameterName, baseElement, ignoreParameters);
final Long workingOfficeId = helper.extractLongNamed(officeIdParameterName, workingElement, ignoreParameters);
if (!workingOfficeId.equals(baseOfficeId)) {
officeId = workingOfficeId;
parametersPassedInRequest.add(officeIdParameterName);
}
}
final String externalIdParamName = "externalId";
if (externalIdParamName.equalsIgnoreCase(parameter)) {
final String baseExternalId = helper.extractStringNamed(externalIdParamName, baseElement, ignoreParameters);
final String workingExternalId = helper.extractStringNamed(externalIdParamName, workingElement, ignoreParameters);
if (!baseExternalId.equals(workingExternalId)) {
externalId = workingExternalId;
parametersPassedInRequest.add(externalIdParamName);
}
}
final String firstnameParamName = "firstname";
if (firstnameParamName.equalsIgnoreCase(parameter)) {
final String baseValue = helper.extractStringNamed(firstnameParamName, baseElement, ignoreParameters);
final String workingCopyValue = helper.extractStringNamed(firstnameParamName, workingElement, ignoreParameters);
if (!baseValue.equals(workingCopyValue)) {
firstname = workingCopyValue;
parametersPassedInRequest.add(firstnameParamName);
}
}
final String lastnameParamName = "lastname";
if (lastnameParamName.equalsIgnoreCase(parameter)) {
final String baseValue = helper.extractStringNamed(lastnameParamName, baseElement, ignoreParameters);
final String workingCopyValue = helper.extractStringNamed(lastnameParamName, workingElement, ignoreParameters);
if (!baseValue.equals(workingCopyValue)) {
lastname = workingCopyValue;
parametersPassedInRequest.add(lastnameParamName);
}
}
final String clientOrBusinessNameParamName = "clientOrBusinessName";
if (clientOrBusinessNameParamName.equalsIgnoreCase(parameter)) {
final String baseValue = helper.extractStringNamed(clientOrBusinessNameParamName, baseElement, ignoreParameters);
final String workingCopyValue = helper.extractStringNamed(clientOrBusinessNameParamName, workingElement, ignoreParameters);
if (!baseValue.equals(workingCopyValue)) {
clientOrBusinessName = workingCopyValue;
parametersPassedInRequest.add(clientOrBusinessNameParamName);
}
}
final String joinedDateParamName = "joinedDate";
if (joinedDateParamName.equalsIgnoreCase(parameter)) {
final LocalDate baseValue = helper.extractLocalDateAsArrayNamed(joinedDateParamName, baseElement, ignoreParameters);
final LocalDate workingCopyValue = helper.extractLocalDateAsArrayNamed(joinedDateParamName, workingElement, ignoreParameters);
if (!baseValue.isEqual(workingCopyValue)) {
joiningDate = workingCopyValue;
parametersPassedInRequest.add(joinedDateParamName);
}
}
}
return new ClientCommand(parametersPassedInRequest, resourceIdentifier, externalId, firstname, lastname, clientOrBusinessName, officeId, joiningDate, false);
}
@Override
public ClientCommand convertApiRequestJsonToClientCommand(final Long resourceIdentifier, final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
@@ -385,7 +475,7 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo
final Map<String, String> requestMap = gsonConverter.fromJson(json, typeOfMap);
final Set<String> supportedParams = new HashSet<String>(
Arrays.asList("externalId", "firstname", "lastname", "clientOrBusinessName", "officeId", "joiningDate", "locale", "dateFormat")
Arrays.asList("id", "externalId", "firstname", "lastname", "clientOrBusinessName", "officeId", "joiningDate", "locale", "dateFormat")
);
checkForUnsupportedParameters(requestMap, supportedParams);
@@ -402,7 +492,46 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo
final String clientOrBusinessName = helper.extractStringNamed("clientOrBusinessName", element, parametersPassedInRequest);
final LocalDate joiningDate = helper.extractLocalDateNamed("joiningDate", element, parametersPassedInRequest);
return new ClientCommand(parametersPassedInRequest, resourceIdentifier, externalId, firstname, lastname, clientOrBusinessName, officeId, joiningDate, makerCheckerApproval);
return new ClientCommand(parametersPassedInRequest, resourceIdentifier, externalId, firstname, lastname, clientOrBusinessName, officeId, joiningDate, false);
}
@Override
public ClientCommand convertInternalJsonFormatToClientCommand(final Long resourceIdentifier, final String json, final boolean checkerApproved) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>(){}.getType();
final Map<String, Object> requestMap = gsonConverter.fromJson(json, typeOfMap);
final Set<String> supportedParams = new HashSet<String>(
Arrays.asList("id", "externalId", "firstname", "lastname", "clientOrBusinessName", "officeId", "joiningDate")
);
checkForUnsupportedParameters(requestMap, supportedParams);
final JsonParser parser = new JsonParser();
final JsonElement element = parser.parse(json);
final JsonParserHelper helper = new JsonParserHelper();
final Set<String> parametersPassedInRequest = new HashSet<String>();
final Long officeId = helper.extractLongNamed("officeId", element, parametersPassedInRequest);
final String externalId = helper.extractStringNamed("externalId", element, parametersPassedInRequest);
final String firstname = helper.extractStringNamed("firstname", element, parametersPassedInRequest);
final String lastname = helper.extractStringNamed("lastname", element, parametersPassedInRequest);
final String clientOrBusinessName = helper.extractStringNamed("clientOrBusinessName", element, parametersPassedInRequest);
final LocalDate joiningDate = helper.extractLocalDateAsArrayNamed("joiningDate", element, parametersPassedInRequest);
return new ClientCommand(parametersPassedInRequest, resourceIdentifier, externalId, firstname, lastname, clientOrBusinessName, officeId, joiningDate, checkerApproved);
}
@Override
public ClientData convertInternalJsonFormatToClientDataChange(final Long resourceIdentifier, final String json) {
final ClientCommand command = convertInternalJsonFormatToClientCommand(resourceIdentifier, json, false);
return ClientData.dataChangeInstance(resourceIdentifier, command.getOfficeId(), command.getExternalId(),
command.getFirstname(), command.getLastname(), command.getClientOrBusinessName(), command.getJoiningDate());
}
@Override
@@ -5,6 +5,7 @@ import java.util.Set;
import org.mifosng.platform.accounting.api.data.ChartOfAccountsData;
import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.api.commands.ClientCommand;
import org.mifosng.platform.api.data.AdditionalFieldsSetData;
import org.mifosng.platform.api.data.AppUserData;
import org.mifosng.platform.api.data.AuthenticatedUserData;
@@ -91,10 +92,14 @@ public interface PortfolioApiJsonSerializerService {
String serializeDepositAccountDataToJson(boolean prettyPrint, Set<String> responseParameters, DepositAccountData account);
String serializeClientDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<ClientData> clients);
String serializeClientCommandToJson(ClientCommand command);
String serializeClientDataToJson(ClientData clientData);
String serializeClientDataToJson(boolean prettyPrint, Set<String> responseParameters, ClientData clientData);
String serializeClientDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<ClientData> clients);
String serializeClientAccountSummaryCollectionDataToJson(boolean prettyPrint, Set<String> responseParameters, ClientAccountSummaryCollectionData clientAccount);
String serializeGroupAccountSummaryCollectionDataToJson(boolean prettyPrint, Set<String> responseParameters, GroupAccountSummaryCollectionData groupAccount);
@@ -157,7 +157,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
final String officeName = rs.getString("officeName");
return new ClientData(officeId, officeName, id, firstname,
lastname, displayName, externalId, joinedDate,imageKey, null, null);
lastname, displayName, externalId, joinedDate,imageKey, null, null, null);
}
}
@@ -18,6 +18,7 @@ import org.mifosng.platform.api.data.ApiParameterError;
import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
import org.springframework.format.number.NumberFormatter;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
@@ -176,6 +177,32 @@ public class JsonParserHelper {
}
return clientApplicationLocale;
}
/**
* Used with the local date is in array format
*/
public LocalDate extractLocalDateAsArrayNamed(final String parameterName, final JsonElement element, final Set<String> parametersPassedInCommand) {
LocalDate value = null;
if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject();
if (object.has(parameterName) && object.get(parameterName).isJsonArray()) {
parametersPassedInCommand.add(parameterName);
final JsonArray dateArray = object.get(parameterName).getAsJsonArray();
Integer year = dateArray.get(0).getAsInt();
Integer month = dateArray.get(1).getAsInt();
Integer day = dateArray.get(2).getAsInt();
value = new LocalDate().withYearOfEra(year).withMonthOfYear(month).withDayOfMonth(day);
}
}
return value;
}
public LocalDate extractLocalDateNamed(
final String parameterName,
@@ -56,10 +56,9 @@ public class CommandSource extends AbstractPersistable<Long> {
final String apiOperation,
final String resource,
final Long resourceId,
final String jsonRequestBody,
final AppUser maker,
final LocalDate madeOnDate) {
return new CommandSource(apiOperation, resource, resourceId, jsonRequestBody, maker, madeOnDate);
return new CommandSource(apiOperation, resource, resourceId, maker, madeOnDate);
}
protected CommandSource() {
@@ -70,13 +69,11 @@ public class CommandSource extends AbstractPersistable<Long> {
final String apiOperation,
final String resource,
final Long resourceId,
final String jsonRequestBody,
final AppUser maker,
final LocalDate madeOnDate) {
this.apiOperation = StringUtils.defaultIfEmpty(apiOperation, null);
this.resource = StringUtils.defaultIfEmpty(resource, null);
this.resourceId = resourceId;
this.commandAsJson = StringUtils.defaultIfEmpty(jsonRequestBody, null);
this.maker = maker;
this.madeOnDate = madeOnDate.toDate();
}
@@ -90,6 +87,10 @@ public class CommandSource extends AbstractPersistable<Long> {
this.resourceId = resourceId;
}
public void updateJsonTo(final String json) {
this.commandAsJson = json;
}
public Long resourceId() {
return this.resourceId;
}
@@ -2,8 +2,11 @@ package org.mifosng.platform.makerchecker.service;
import org.joda.time.LocalDate;
import org.mifosng.platform.api.commands.ClientCommand;
import org.mifosng.platform.api.data.ClientData;
import org.mifosng.platform.api.data.EntityIdentifier;
import org.mifosng.platform.api.infrastructure.PortfolioApiDataConversionService;
import org.mifosng.platform.api.infrastructure.PortfolioApiJsonSerializerService;
import org.mifosng.platform.client.service.ClientReadPlatformService;
import org.mifosng.platform.client.service.ClientWritePlatformService;
import org.mifosng.platform.client.service.RollbackTransactionAsCommandIsNotApprovedByCheckerException;
import org.mifosng.platform.makerchecker.domain.CommandSource;
@@ -14,71 +17,91 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
*
*/
@Service
public class PortfolioCommandSourceWriteServiceImpl implements PortfolioCommandSourceWritePlatformService {
private final PlatformSecurityContext context;
private final CommandSourceRepository commandSourceRepository;
private final PortfolioApiDataConversionService apiDataConversionService;
private final PortfolioApiJsonSerializerService apiJsonSerializerService;
private final ClientWritePlatformService clientWritePlatformService;
private final ClientReadPlatformService clientReadPlatformService;
@Autowired
public PortfolioCommandSourceWriteServiceImpl(final PlatformSecurityContext context, final CommandSourceRepository makerCheckerRepository,
final PortfolioApiDataConversionService apiDataConversionService,
final ClientWritePlatformService clientWritePlatformService) {
final PortfolioApiJsonSerializerService apiJsonSerializerService,
final ClientWritePlatformService clientWritePlatformService,
final ClientReadPlatformService clientReadPlatformService) {
this.context = context;
this.commandSourceRepository = makerCheckerRepository;
this.apiDataConversionService = apiDataConversionService;
this.apiJsonSerializerService = apiJsonSerializerService;
this.clientWritePlatformService = clientWritePlatformService;
this.clientReadPlatformService = clientReadPlatformService;
}
// NOT TRANSACTIONAL BY DESIGN FOR NOW
@Override
public EntityIdentifier logCommandSource(final String taskOperation, final String taskEntity, final Long entityId, final String taskJson) {
public EntityIdentifier logCommandSource(final String apiOperation, final String resource, final Long resourceId, final String jsonRequestBody) {
final AppUser maker = context.authenticatedUser();
final LocalDate asToday = new LocalDate();
final CommandSource entity = CommandSource.createdBy(taskOperation, taskEntity, entityId, taskJson, maker, asToday);
final CommandSource commandSource = CommandSource.createdBy(apiOperation, resource, resourceId, maker, asToday);
final boolean makerCheckerApproval = false;
Long resourceId = null;
if (entity.isClientResource()) {
final ClientCommand command = this.apiDataConversionService.convertJsonToClientCommand(entityId, entity.json(), makerCheckerApproval);
if (entity.isCreate()) {
Long newResourceId = null;
if (commandSource.isClientResource()) {
// translate incoming api request json into java object catering for local and dateFormat parts of api
final ClientCommand command = this.apiDataConversionService.convertApiRequestJsonToClientCommand(resourceId, jsonRequestBody);
// produce serialized json of internal java object representation for persistence.
final String internalCommandSerializedAsJson = this.apiJsonSerializerService.serializeClientCommandToJson(command);
commandSource.updateJsonTo(internalCommandSerializedAsJson);
if (commandSource.isCreate()) {
try {
resourceId = this.clientWritePlatformService.createClient(command);
entity.markAsChecked(maker, asToday);
entity.updateResourceId(resourceId);
newResourceId = this.clientWritePlatformService.createClient(command);
commandSource.markAsChecked(maker, asToday);
commandSource.updateResourceId(newResourceId);
} catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) {
// swallow this rollback transaction by design
}
} else if (entity.isUpdate()) {
} else if (commandSource.isUpdate()) {
try {
EntityIdentifier result = this.clientWritePlatformService.updateClientDetails(command);
resourceId = result.getEntityId();
entity.markAsChecked(maker, asToday);
// useful to employ change detection on update scenario to only store what details have changed
final ClientData originalClient = this.clientReadPlatformService.retrieveIndividualClient(resourceId);
final ClientData changedClient = this.apiDataConversionService.convertInternalJsonFormatToClientDataChange(resourceId, internalCommandSerializedAsJson);
final String baseJson = this.apiJsonSerializerService.serializeClientDataToJson(originalClient);
final String workingJson = this.apiJsonSerializerService.serializeClientDataToJson(changedClient);
final ClientCommand changesOnly = this.apiDataConversionService.detectChanges(resourceId, baseJson, workingJson);
final String changesOnlyJson = this.apiJsonSerializerService.serializeClientCommandToJson(changesOnly);
commandSource.updateJsonTo(changesOnlyJson);
EntityIdentifier result = this.clientWritePlatformService.updateClientDetails(changesOnly);
newResourceId = result.getEntityId();
commandSource.markAsChecked(maker, asToday);
} catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) {
// swallow this rollback transaction by design
}
} else if (entity.isDelete()) {
} else if (commandSource.isDelete()) {
try {
EntityIdentifier result = this.clientWritePlatformService.deleteClient(command);
resourceId = result.getEntityId();
entity.markAsChecked(maker, asToday);
newResourceId = result.getEntityId();
commandSource.markAsChecked(maker, asToday);
} catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) {
// swallow this rollback transaction by design
}
}
}
commandSourceRepository.save(entity);
commandSourceRepository.save(commandSource);
return EntityIdentifier.makerChecker(resourceId, entity.getId());
return EntityIdentifier.makerChecker(newResourceId, commandSource.getId());
}
@Transactional
@@ -92,7 +115,7 @@ public class PortfolioCommandSourceWriteServiceImpl implements PortfolioCommandS
Long resourceId = null;
if (entity.isClientResource()) {
final ClientCommand command = this.apiDataConversionService.convertJsonToClientCommand(entity.resourceId(), entity.json(), true);
final ClientCommand command = this.apiDataConversionService.convertInternalJsonFormatToClientCommand(entity.resourceId(), entity.json(), true);
if (entity.isCreate()) {
resourceId = this.clientWritePlatformService.createClient(command);
} else if (entity.isUpdate()) {
@@ -10,4 +10,6 @@ public interface PortfolioCommandsReadPlatformService {
CommandSourceData retrieveById(Long id);
Collection<CommandSourceData> retrieveUnprocessChangesByResourceId(String apiResource, Long clientId);
}
@@ -69,4 +69,13 @@ public class PortfolioCommandsReadPlatformServiceImpl implements PortfolioComman
return this.jdbcTemplate.queryForObject(sql, rm, new Object[] {id});
}
@Override
public Collection<CommandSourceData> retrieveUnprocessChangesByResourceId(final String apiResource, final Long resourceId) {
final CommandSourceMapper rm = new CommandSourceMapper();
final String sql = "select " + rm.schema() + " where mc.api_resource like ? and mc.resource_id = ? and mc.checker_id is null order by mc.made_on_date DESC, mc.api_operation ASC";
return this.jdbcTemplate.query(sql, rm, new Object[] {apiResource, resourceId});
}
}
@@ -12,6 +12,6 @@ public interface CodeWritePlatformService {
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'ORGANISATION_ADMINISTRATION_SUPER_USER', 'UPDATE_CODE)")
Long updateCode(final CodeCommand command);
@PreAuthorize(value = "hasAnyRole('ORGANISATION_ADMINISTRATION_SUPER_USER_ROLE')")
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'ORGANISATION_ADMINISTRATION_SUPER_USER_ROLE', 'DELETE_CODE')")
EntityIdentifier deleteCode(Long codeId);
}