MIFOSX-97: refactor into command handling logic, prepare for sql based approach for change detection.

This commit is contained in:
Keith Woodlock
2012-11-21 14:23:16 +00:00
parent b8961d7914
commit 9aa9b870f0
16 changed files with 2731 additions and 2920 deletions
@@ -68,8 +68,7 @@ public interface PortfolioApiDataConversionService {
LoanTransactionCommand convertJsonToLoanTransactionCommand(Long resourceIdentifier, String json);
AdjustLoanTransactionCommand convertJsonToAdjustLoanTransactionCommand(
Long loanId, Long transactionId, String json);
AdjustLoanTransactionCommand convertJsonToAdjustLoanTransactionCommand(Long loanId, Long transactionId, String json);
OrganisationCurrencyCommand convertJsonToOrganisationCurrencyCommand(String json);
@@ -95,5 +94,5 @@ public interface PortfolioApiDataConversionService {
CodeCommand convertJsonToCodeCommand(Long resourceIdentifier, String json);
SavingAccountCommand convertJsonToSavingAccountCommand(Long resourceIdentifier,String json);
SavingAccountCommand convertJsonToSavingAccountCommand(Long resourceIdentifier, String json);
}
@@ -66,7 +66,8 @@ public interface PortfolioApiJsonSerializerService {
String serializeOfficeDataToJson(boolean prettyPrint, Set<String> responseParameters, OfficeData office);
String serializeOfficeTransactionDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<OfficeTransactionData> officeTransactions);
String serializeOfficeTransactionDataToJson(boolean prettyPrint, Set<String> responseParameters,
Collection<OfficeTransactionData> officeTransactions);
String serializeOfficeTransactionDataToJson(boolean prettyPrint, Set<String> responseParameters, OfficeTransactionData officeTransaction);
@@ -100,9 +101,11 @@ public interface PortfolioApiJsonSerializerService {
String serializeClientDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<ClientData> clients);
String serializeClientAccountSummaryCollectionDataToJson(boolean prettyPrint, Set<String> responseParameters, ClientAccountSummaryCollectionData clientAccount);
String serializeClientAccountSummaryCollectionDataToJson(boolean prettyPrint, Set<String> responseParameters,
ClientAccountSummaryCollectionData clientAccount);
String serializeGroupAccountSummaryCollectionDataToJson(boolean prettyPrint, Set<String> responseParameters, GroupAccountSummaryCollectionData groupAccount);
String serializeGroupAccountSummaryCollectionDataToJson(boolean prettyPrint, Set<String> responseParameters,
GroupAccountSummaryCollectionData groupAccount);
String serializeGroupDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<GroupData> groups);
@@ -118,7 +121,8 @@ public interface PortfolioApiJsonSerializerService {
String serializeLoanTransactionDataToJson(boolean prettyPrint, Set<String> responseParameters, LoanTransactionData newTransactionData);
String serializeLoanReassignmentDataToJson(boolean prettyPrint, Set<String> responseParameters, LoanReassignmentData loanReassignmentData);
String serializeLoanReassignmentDataToJson(boolean prettyPrint, Set<String> responseParameters,
LoanReassignmentData loanReassignmentData);
String serializeChargeDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<ChargeData> charges);
@@ -132,24 +136,27 @@ public interface PortfolioApiJsonSerializerService {
String serializeEntityIdentifier(EntityIdentifier identifier);
String serializeClientIdentifierDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<ClientIdentifierData> clientIdentifiers);
String serializeClientIdentifierDataToJson(boolean prettyPrint, Set<String> responseParameters,
Collection<ClientIdentifierData> clientIdentifiers);
String serializeClientIdentifierDataToJson(boolean prettyPrint, Set<String> responseParameters, ClientIdentifierData clientIdentifierData);
String serializeClientIdentifierDataToJson(boolean prettyPrint, Set<String> responseParameters,
ClientIdentifierData clientIdentifierData);
String serializeDocumentDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<DocumentData> documentDatas);
String serializeDocumentDataToJson(boolean prettyPrint, Set<String> responseParameters, DocumentData documentData);
String serializeCodeDataToJson(boolean prettyPrint,Set<String> responseParameters, Collection<CodeData> codes);
String serializeCodeDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<CodeData> codes);
String serializeCodeDataToJson(boolean prettyPrint,Set<String> responseParameters, CodeData code);
String serializeCodeDataToJson(boolean prettyPrint, Set<String> responseParameters, CodeData code);
String serializeMakerCheckerDataToJson(boolean prettyPrint,Set<String> responseParameters, Collection<CommandSourceData> entries);
String serializeMakerCheckerDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<CommandSourceData> entries);
String serializeSavingAccountsDataToJson(boolean prettyPrint, Set<String> responseParameters, SavingAccountData account);
String serializeSavingAccountsDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<SavingAccountData> accounts);
// TODO - KW - might devide serialize up into portfolio and account to reduce interface
// TODO - KW - might devide serialize up into portfolio and account to
// reduce interface
String serializeChartOfAccountDataToJson(boolean prettyPrint, Set<String> responseParameters, ChartOfAccountsData chartOfAccounts);
}
@@ -49,17 +49,14 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
private final NoteRepository noteRepository;
private final CodeValueRepository codeValueRepository;
// FIXME - kw - this value is hardcoded for now but will be retrieved from configuration
// FIXME - kw - this value is hardcoded for now but will be retrieved from
// configuration
private boolean makerCheckerEnabledForTask = false;
@Autowired
public ClientWritePlatformServiceJpaRepositoryImpl(
final PlatformSecurityContext context,
final ClientRepository clientRepository,
final ClientIdentifierRepository clientIdentifierRepository,
final OfficeRepository officeRepository,
NoteRepository noteRepository,
final CodeValueRepository codeValueRepository) {
public ClientWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final ClientRepository clientRepository,
final ClientIdentifierRepository clientIdentifierRepository, final OfficeRepository officeRepository,
NoteRepository noteRepository, final CodeValueRepository codeValueRepository) {
this.context = context;
this.clientRepository = clientRepository;
this.clientIdentifierRepository = clientIdentifierRepository;
@@ -74,16 +71,12 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
final Long clientId = command.getId();
final Client client = this.clientRepository.findOne(clientId);
if (client == null || client.isDeleted()) {
throw new ClientNotFoundException(clientId);
}
if (client == null || client.isDeleted()) { throw new ClientNotFoundException(clientId); }
client.delete();
this.clientRepository.save(client);
if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) {
throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException();
}
if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
return new EntityIdentifier(client.getId());
}
@@ -92,21 +85,15 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
* Guaranteed to throw an exception no matter what the data integrity issue
* is.
*/
private void handleDataIntegrityIssues(final ClientCommand command,
final DataIntegrityViolationException dve) {
private void handleDataIntegrityIssues(final ClientCommand command, final DataIntegrityViolationException dve) {
Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("external_id")) {
throw new PlatformDataIntegrityException(
"error.msg.client.duplicate.externalId",
"Client with externalId `" + command.getExternalId()
+ "` already exists", "externalId",
command.getExternalId());
}
if (realCause.getMessage().contains("external_id")) { throw new PlatformDataIntegrityException(
"error.msg.client.duplicate.externalId", "Client with externalId `" + command.getExternalId() + "` already exists",
"externalId", command.getExternalId()); }
logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException(
"error.msg.client.unknown.data.integrity.issue",
throw new PlatformDataIntegrityException("error.msg.client.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
}
@@ -121,9 +108,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
validator.validateForCreate();
final Office clientOffice = this.officeRepository.findOne(command.getOfficeId());
if (clientOffice == null) {
throw new OfficeNotFoundException(command.getOfficeId());
}
if (clientOffice == null) { throw new OfficeNotFoundException(command.getOfficeId()); }
String firstname = command.getFirstname();
String lastname = command.getLastname();
@@ -132,15 +117,11 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
firstname = null;
}
final Client newClient = Client
.newClient(clientOffice, firstname, lastname,
command.getJoiningDate(), command.getExternalId());
final Client newClient = Client.newClient(clientOffice, firstname, lastname, command.getJoiningDate(), command.getExternalId());
this.clientRepository.save(newClient);
if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) {
throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException();
}
if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
return newClient.getId();
} catch (DataIntegrityViolationException dve) {
@@ -163,22 +144,16 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
Long officeId = command.getOfficeId();
if (command.isOfficeChanged() && officeId != null) {
clientOffice = this.officeRepository.findOne(officeId);
if (clientOffice == null) {
throw new OfficeNotFoundException(command.getOfficeId());
}
if (clientOffice == null) { throw new OfficeNotFoundException(command.getOfficeId()); }
}
final Client clientForUpdate = this.clientRepository.findOne(command.getId());
if (clientForUpdate == null || clientForUpdate.isDeleted()) {
throw new ClientNotFoundException(command.getId());
}
if (clientForUpdate == null || clientForUpdate.isDeleted()) { throw new ClientNotFoundException(command.getId()); }
clientForUpdate.update(clientOffice, command);
this.clientRepository.saveAndFlush(clientForUpdate);
if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) {
throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException();
}
if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) { throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); }
return new EntityIdentifier(clientForUpdate.getId());
} catch (DataIntegrityViolationException dve) {
@@ -194,9 +169,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
context.authenticatedUser();
final Client clientForUpdate = this.clientRepository.findOne(command.getClientId());
if (clientForUpdate == null) {
throw new ClientNotFoundException(command.getClientId());
}
if (clientForUpdate == null) { throw new ClientNotFoundException(command.getClientId()); }
final Note note = Note.clientNote(clientForUpdate, command.getNote());
@@ -212,10 +185,8 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
context.authenticatedUser();
final Note noteForUpdate = this.noteRepository.findOne(command.getId());
if (noteForUpdate == null
|| noteForUpdate.isNotAgainstClientWithIdOf(command.getClientId())) {
throw new NoteNotFoundException(command.getId(),command.getClientId(), "client");
}
if (noteForUpdate == null || noteForUpdate.isNotAgainstClientWithIdOf(command.getClientId())) { throw new NoteNotFoundException(
command.getId(), command.getClientId(), "client"); }
noteForUpdate.update(command.getNote());
@@ -237,18 +208,15 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
validator.validateForCreate();
final Client client = this.clientRepository.findOne(command.getClientId());
if (client == null || client.isDeleted()) {
throw new ClientNotFoundException(command.getClientId());
}
if (client == null || client.isDeleted()) { throw new ClientNotFoundException(command.getClientId()); }
final CodeValue documentType = this.codeValueRepository.findOne(command.getDocumentTypeId());
if (documentType == null) {
throw new CodeValueNotFoundException(command.getDocumentTypeId());
}
if (documentType == null) { throw new CodeValueNotFoundException(command.getDocumentTypeId()); }
documentTypeId = documentType.getId();
documentTypeLabel = documentType.getLabel();
final ClientIdentifier clientIdentifier = ClientIdentifier.createNew(client, documentType, documentKey, command.getDescription());
final ClientIdentifier clientIdentifier = ClientIdentifier.createNew(client, documentType, documentKey,
command.getDescription());
this.clientIdentifierRepository.save(clientIdentifier);
@@ -277,19 +245,16 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
documentTypeId = command.getDocumentTypeId();
if (command.isDocumentTypeChanged() && documentTypeId != null) {
documentType = this.codeValueRepository.findOne(documentTypeId);
if (documentType == null) {
throw new CodeValueNotFoundException(command.getDocumentTypeId());
}
if (documentType == null) { throw new CodeValueNotFoundException(command.getDocumentTypeId()); }
documentTypeId = documentType.getId();
documentTypeLabel = documentType.getLabel();
}
final ClientIdentifier clientIdentifierForUpdate = this.clientIdentifierRepository.findOne(command.getId());
if (clientIdentifierForUpdate == null) {
throw new ClientIdentifierNotFoundException(command.getId());
}
if (clientIdentifierForUpdate == null) { throw new ClientIdentifierNotFoundException(command.getId()); }
// TODO - KW - why need to check what changed when integrity violation occurs?
// TODO - KW - why need to check what changed when integrity
// violation occurs?
if (command.isDocumentTypeChanged() && command.isDocumentKeyChanged()) {
documentTypeId = command.getDocumentTypeId();
documentKey = command.getDocumentKey();
@@ -312,22 +277,17 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
}
}
private void handleClientIdentifierDataIntegrityViolation(
final String documentTypeLabel,
final Long documentTypeId,
final String documentKey,
final DataIntegrityViolationException dve) {
private void handleClientIdentifierDataIntegrityViolation(final String documentTypeLabel, final Long documentTypeId,
final String documentKey, final DataIntegrityViolationException dve) {
if (dve.getMostSpecificCause().getMessage().contains("unique_client_identifier")) {
throw new DuplicateClientIdentifierException(documentTypeLabel);
} else if (dve.getMostSpecificCause().getMessage().contains("unique_identifier_key")) {
throw new DuplicateClientIdentifierException(documentTypeId, documentTypeLabel, documentKey);
}
} else if (dve.getMostSpecificCause().getMessage().contains("unique_identifier_key")) { throw new DuplicateClientIdentifierException(
documentTypeId, documentTypeLabel, documentKey); }
// only log as error if unexpected data integrity violation.
logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException(
"error.msg.clientIdentifier.unknown.data.integrity.issue",
throw new PlatformDataIntegrityException("error.msg.clientIdentifier.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
}
@@ -335,9 +295,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
@Override
public EntityIdentifier deleteClientIdentifier(final Long clientIdentifierId) {
final ClientIdentifier clientIdentifier = this.clientIdentifierRepository.findOne(clientIdentifierId);
if (clientIdentifier == null) {
throw new ClientIdentifierNotFoundException(clientIdentifierId);
}
if (clientIdentifier == null) { throw new ClientIdentifierNotFoundException(clientIdentifierId); }
this.clientIdentifierRepository.delete(clientIdentifier);
return new EntityIdentifier(clientIdentifierId);
@@ -345,16 +303,12 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
@Transactional
@Override
public EntityIdentifier saveOrUpdateClientImage(Long clientId,
String imageName, InputStream inputStream) {
public EntityIdentifier saveOrUpdateClientImage(Long clientId, String imageName, InputStream inputStream) {
try {
final Client client = this.clientRepository.findOne(clientId);
String imageUploadLocation = setupForClientImageUpdate(clientId,
client);
String imageLocation = FileUtils.saveToFileSystem(inputStream,
imageUploadLocation, imageName);
String imageUploadLocation = setupForClientImageUpdate(clientId, client);
String imageLocation = FileUtils.saveToFileSystem(inputStream, imageUploadLocation, imageName);
return updateClientImage(clientId, client, imageLocation);
} catch (IOException ioe) {
@@ -368,9 +322,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
public EntityIdentifier deleteClientImage(final Long clientId) {
final Client client = this.clientRepository.findOne(clientId);
if (client == null || client.isDeleted()) {
throw new ClientNotFoundException(clientId);
}
if (client == null || client.isDeleted()) { throw new ClientNotFoundException(clientId); }
// delete image from the file system
if (StringUtils.isNotEmpty(client.getImageKey())) {
@@ -395,9 +347,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
}
private String setupForClientImageUpdate(final Long clientId, final Client client) {
if (client == null || client.isDeleted()) {
throw new ClientNotFoundException(clientId);
}
if (client == null || client.isDeleted()) { throw new ClientNotFoundException(clientId); }
final String imageUploadLocation = FileUtils.generateClientImageParentDirectory(clientId);
// delete previous image from the file system
@@ -0,0 +1,19 @@
package org.mifosng.platform.infrastructure.errorhandling;
/**
* A {@link RuntimeException} that is thrown in the case where an invalid or unknown
* command is attempted to be processed by platform.
*/
public class UnsupportedCommandException extends RuntimeException {
private final String unsupportedCommandName;
public UnsupportedCommandException(final String unsupportedCommandName) {
this.unsupportedCommandName = unsupportedCommandName;
}
public String getUnsupportedCommandName() {
return unsupportedCommandName;
}
}
@@ -0,0 +1,42 @@
package org.mifosng.platform.infrastructure.errorhandling;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import org.mifosng.platform.api.data.ApiGlobalErrorResponse;
import org.mifosng.platform.api.data.ApiParameterError;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
/**
* An {@link ExceptionMapper} to map {@link UnsupportedCommandException} thrown by platform into a HTTP API friendly format.
*/
@Provider
@Component
@Scope("singleton")
public class UnsupportedCommandExceptionMapper implements ExceptionMapper<UnsupportedCommandException> {
@Override
public Response toResponse(final UnsupportedCommandException exception) {
List<ApiParameterError> errors = new ArrayList<ApiParameterError>();
StringBuilder validationErrorCode = new StringBuilder("error.msg.command.unsupported");
StringBuilder defaultEnglishMessage = new StringBuilder("The command ").append(exception.getUnsupportedCommandName()).append(" is not supported.");
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), exception.getUnsupportedCommandName(), exception.getUnsupportedCommandName());
errors.add(error);
ApiGlobalErrorResponse invalidParameterError = ApiGlobalErrorResponse.badClientRequest(
"validation.msg.validation.errors.exist",
"Validation errors exist.", errors);
return Response.status(Status.BAD_REQUEST).entity(invalidParameterError).type(MediaType.APPLICATION_JSON).build();
}
}
@@ -31,13 +31,10 @@ public class CommandSource extends AbstractPersistable<Long> {
@Column(name = "command_as_json", length = 1000)
private String commandAsJson;
// should maker and checker be a staff member as opposed to just any application user?
@SuppressWarnings("unused")
@ManyToOne
@JoinColumn(name = "maker_id", nullable = false)
private AppUser maker;
@SuppressWarnings("unused")
@Column(name = "made_on_date", nullable = false)
@Temporal(TemporalType.DATE)
private Date madeOnDate;
@@ -52,11 +49,7 @@ public class CommandSource extends AbstractPersistable<Long> {
@Temporal(TemporalType.DATE)
private Date checkedOnDate;
public static CommandSource createdBy(
final String apiOperation,
final String resource,
final Long resourceId,
final AppUser maker,
public static CommandSource createdBy(final String apiOperation, final String resource, final Long resourceId, final AppUser maker,
final LocalDate madeOnDate) {
return new CommandSource(apiOperation, resource, resourceId, maker, madeOnDate);
}
@@ -65,11 +58,7 @@ public class CommandSource extends AbstractPersistable<Long> {
//
}
private CommandSource(
final String apiOperation,
final String resource,
final Long resourceId,
final AppUser maker,
private CommandSource(final String apiOperation, final String resource, final Long resourceId, final AppUser maker,
final LocalDate madeOnDate) {
this.apiOperation = StringUtils.defaultIfEmpty(apiOperation, null);
this.resource = StringUtils.defaultIfEmpty(resource, null);
@@ -78,6 +67,14 @@ public class CommandSource extends AbstractPersistable<Long> {
this.madeOnDate = madeOnDate.toDate();
}
public CommandSource copy() {
LocalDate madeOnLocalDate = null;
if (this.madeOnDate != null) {
madeOnLocalDate = new LocalDate(this.madeOnDate);
}
return new CommandSource(this.apiOperation, this.resource, this.resourceId, this.maker, madeOnLocalDate);
}
public void markAsChecked(final AppUser checker, final LocalDate checkedOnDate) {
this.checker = checker;
this.checkedOnDate = checkedOnDate.toDate();
@@ -99,6 +96,18 @@ public class CommandSource extends AbstractPersistable<Long> {
return this.commandAsJson;
}
public String commandName() {
return this.apiOperation + '-' + this.resource;
}
public String resourceName() {
return this.resource;
}
public String operation() {
return this.apiOperation;
}
public boolean isClientResource() {
return this.resource.equalsIgnoreCase("CLIENTS");
}
@@ -0,0 +1,7 @@
package org.mifosng.platform.makerchecker.service;
public interface ChangeDetectionService {
String detectChanges(String operation, String resourceName, Long resourceId, String commandSerializedAsJson);
}
@@ -0,0 +1,48 @@
package org.mifosng.platform.makerchecker.service;
import org.mifosng.platform.api.commands.ClientCommand;
import org.mifosng.platform.api.data.ClientData;
import org.mifosng.platform.api.infrastructure.PortfolioApiDataConversionService;
import org.mifosng.platform.api.infrastructure.PortfolioApiJsonSerializerService;
import org.mifosng.platform.client.service.ClientReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ChangeDetectionServiceJdbc implements ChangeDetectionService {
private final PortfolioApiDataConversionService apiDataConversionService;
private final PortfolioApiJsonSerializerService apiJsonSerializerService;
private final ClientReadPlatformService clientReadPlatformService;
@Autowired
public ChangeDetectionServiceJdbc(final PortfolioApiDataConversionService apiDataConversionService,
final PortfolioApiJsonSerializerService apiJsonSerializerService, final ClientReadPlatformService clientReadPlatformService) {
this.apiDataConversionService = apiDataConversionService;
this.apiJsonSerializerService = apiJsonSerializerService;
this.clientReadPlatformService = clientReadPlatformService;
}
@Override
public String detectChanges(final String operation, final String resourceName, final Long resourceId,
final String commandSerializedAsJson) {
String changesOnlyJson = "";
// FIXME - KW/JW - this change detection code for update of clients
// using ClientCommand can re placed when more suitable sql version is
// implemented.
final ClientData originalClient = this.clientReadPlatformService.retrieveIndividualClient(resourceId);
final ClientData changedClient = this.apiDataConversionService.convertInternalJsonFormatToClientDataChange(resourceId,
commandSerializedAsJson);
final String baseJson = this.apiJsonSerializerService.serializeClientDataToJson(originalClient);
final String workingJson = this.apiJsonSerializerService.serializeClientDataToJson(changedClient);
final ClientCommand changesOnly = this.apiDataConversionService.detectChanges(resourceId, baseJson, workingJson);
changesOnlyJson = this.apiJsonSerializerService.serializeClientCommandToJson(changesOnly);
return changesOnlyJson;
}
}
@@ -0,0 +1,113 @@
package org.mifosng.platform.makerchecker.service;
import org.joda.time.LocalDate;
import org.mifosng.platform.api.commands.ClientCommand;
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.ClientWritePlatformService;
import org.mifosng.platform.client.service.RollbackTransactionAsCommandIsNotApprovedByCheckerException;
import org.mifosng.platform.makerchecker.domain.CommandSource;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ClientCommandHandler implements CommandSourceHandler {
private final PlatformSecurityContext context;
private final ChangeDetectionService changeDetectionService;
private final PortfolioApiDataConversionService apiDataConversionService;
private final PortfolioApiJsonSerializerService apiJsonSerializerService;
private final ClientWritePlatformService clientWritePlatformService;
@Autowired
public ClientCommandHandler(final PlatformSecurityContext context, final ChangeDetectionService changeDetectionService,
final PortfolioApiDataConversionService apiDataConversionService,
final PortfolioApiJsonSerializerService apiJsonSerializerService, final ClientWritePlatformService clientWritePlatformService) {
this.context = context;
this.changeDetectionService = changeDetectionService;
this.apiDataConversionService = apiDataConversionService;
this.apiJsonSerializerService = apiJsonSerializerService;
this.clientWritePlatformService = clientWritePlatformService;
}
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 ClientCommand command = this.apiDataConversionService.convertApiRequestJsonToClientCommand(resourceId, apiRequestBodyInJson);
final String commandSerializedAsJson = this.apiJsonSerializerService.serializeClientCommandToJson(command);
commandSourceResult.updateJsonTo(commandSerializedAsJson);
if (commandSource.isCreate()) {
try {
newResourceId = this.clientWritePlatformService.createClient(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.detectChanges(commandSource.operation(),
commandSource.resourceName(), commandSource.resourceId(), commandSerializedAsJson);
commandSourceResult.updateJsonTo(jsonOfChangesOnly);
final ClientCommand changesOnly = this.apiDataConversionService.convertInternalJsonFormatToClientCommand(
commandSource.resourceId(), jsonOfChangesOnly, false);
EntityIdentifier result = this.clientWritePlatformService.updateClientDetails(changesOnly);
newResourceId = result.getEntityId();
commandSourceResult.markAsChecked(maker, asToday);
} catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) {
// swallow this rollback transaction by design
}
} else if (commandSource.isDelete()) {
try {
EntityIdentifier result = this.clientWritePlatformService.deleteClient(command);
newResourceId = result.getEntityId();
commandSourceResult.markAsChecked(maker, asToday);
} catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) {
// swallow this rollback transaction by design
}
}
return commandSourceResult;
}
public CommandSource handle(final CommandSource commandSourceResult) {
Long resourceId = null;
if (commandSourceResult.isClientResource()) {
final ClientCommand command = this.apiDataConversionService.convertInternalJsonFormatToClientCommand(
commandSourceResult.resourceId(), commandSourceResult.json(), true);
if (commandSourceResult.isCreate()) {
resourceId = this.clientWritePlatformService.createClient(command);
} else if (commandSourceResult.isUpdate()) {
// TODO - is there a case for doing change detection at this
// point also?
EntityIdentifier result = this.clientWritePlatformService.updateClientDetails(command);
resourceId = result.getEntityId();
} else if (commandSourceResult.isDelete()) {
EntityIdentifier result = this.clientWritePlatformService.deleteClient(command);
resourceId = result.getEntityId();
}
}
commandSourceResult.updateResourceId(resourceId);
return commandSourceResult;
}
}
@@ -0,0 +1,5 @@
package org.mifosng.platform.makerchecker.service;
public interface CommandSourceHandler {
}
@@ -0,0 +1,48 @@
package org.mifosng.platform.makerchecker.service;
import org.mifosng.platform.infrastructure.errorhandling.UnsupportedCommandException;
import org.mifosng.platform.makerchecker.domain.CommandSource;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* A class to delegate handling of commands to appropriate handler.
*/
@Service
public class CommandSourceHandlerDelegator {
private final PlatformSecurityContext context;
private final ClientCommandHandler clientCommandHandler;
@Autowired
public CommandSourceHandlerDelegator(final PlatformSecurityContext context, final ClientCommandHandler clientCommandHandler) {
this.context = context;
this.clientCommandHandler = clientCommandHandler;
}
public CommandSource handle(final CommandSource commandSource, final String apiRequestBodyInJson) {
context.authenticatedUser();
CommandSource commandSourceResult = null;
if (commandSource.isClientResource()) {
commandSourceResult = clientCommandHandler.handle(commandSource, apiRequestBodyInJson);
} else {
throw new UnsupportedCommandException(commandSource.commandName());
}
return commandSourceResult;
}
public CommandSource handleExistingCommand(final CommandSource commandSource) {
CommandSource commandSourceResult = null;
if (commandSource.isClientResource()) {
commandSourceResult = clientCommandHandler.handle(commandSource);
} else {
throw new UnsupportedCommandException(commandSource.commandName());
}
return commandSourceResult;
}
}
@@ -1,14 +1,7 @@
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;
import org.mifosng.platform.makerchecker.domain.CommandSourceRepository;
import org.mifosng.platform.security.PlatformSecurityContext;
@@ -22,86 +15,30 @@ public class PortfolioCommandSourceWriteServiceImpl implements PortfolioCommandS
private final PlatformSecurityContext context;
private final CommandSourceRepository commandSourceRepository;
private final PortfolioApiDataConversionService apiDataConversionService;
private final PortfolioApiJsonSerializerService apiJsonSerializerService;
private final ClientWritePlatformService clientWritePlatformService;
private final ClientReadPlatformService clientReadPlatformService;
private final CommandSourceHandlerDelegator commandSourceHandlerDelegator;
@Autowired
public PortfolioCommandSourceWriteServiceImpl(final PlatformSecurityContext context, final CommandSourceRepository makerCheckerRepository,
final PortfolioApiDataConversionService apiDataConversionService,
final PortfolioApiJsonSerializerService apiJsonSerializerService,
final ClientWritePlatformService clientWritePlatformService,
final ClientReadPlatformService clientReadPlatformService) {
public PortfolioCommandSourceWriteServiceImpl(final PlatformSecurityContext context,
final CommandSourceRepository makerCheckerRepository,
final CommandSourceHandlerDelegator commandSourceHandlerDelegator) {
this.context = context;
this.commandSourceRepository = makerCheckerRepository;
this.apiDataConversionService = apiDataConversionService;
this.apiJsonSerializerService = apiJsonSerializerService;
this.clientWritePlatformService = clientWritePlatformService;
this.clientReadPlatformService = clientReadPlatformService;
this.commandSourceHandlerDelegator = commandSourceHandlerDelegator;
}
// NOT TRANSACTIONAL BY DESIGN FOR NOW
@Override
public EntityIdentifier logCommandSource(final String apiOperation, final String resource, final Long resourceId, final String jsonRequestBody) {
public EntityIdentifier logCommandSource(final String apiOperation, final String resource, final Long resourceId,
final String apiRequestBodyInJson) {
final AppUser maker = context.authenticatedUser();
final LocalDate asToday = new LocalDate();
final CommandSource commandSource = CommandSource.createdBy(apiOperation, resource, resourceId, maker, asToday);
final CommandSource commandSourceInput = CommandSource.createdBy(apiOperation, resource, resourceId, maker, asToday);
final CommandSource commandSourceResult = this.commandSourceHandlerDelegator.handle(commandSourceInput, apiRequestBodyInJson);
commandSourceRepository.save(commandSourceResult);
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 {
newResourceId = this.clientWritePlatformService.createClient(command);
commandSource.markAsChecked(maker, asToday);
commandSource.updateResourceId(newResourceId);
} catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) {
// swallow this rollback transaction by design
}
} else if (commandSource.isUpdate()) {
try {
// 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 (commandSource.isDelete()) {
try {
EntityIdentifier result = this.clientWritePlatformService.deleteClient(command);
newResourceId = result.getEntityId();
commandSource.markAsChecked(maker, asToday);
} catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) {
// swallow this rollback transaction by design
}
}
}
commandSourceRepository.save(commandSource);
return EntityIdentifier.makerChecker(newResourceId, commandSource.getId());
return EntityIdentifier.makerChecker(commandSourceResult.resourceId(), commandSourceResult.getId());
}
@Transactional
@@ -110,28 +47,14 @@ public class PortfolioCommandSourceWriteServiceImpl implements PortfolioCommandS
final AppUser checker = context.authenticatedUser();
final CommandSource entity = this.commandSourceRepository.findOne(id);
entity.markAsChecked(checker, new LocalDate());
final CommandSource commandSourceInput = this.commandSourceRepository.findOne(id);
commandSourceInput.markAsChecked(checker, new LocalDate());
Long resourceId = null;
if (entity.isClientResource()) {
final ClientCommand command = this.apiDataConversionService.convertInternalJsonFormatToClientCommand(entity.resourceId(), entity.json(), true);
if (entity.isCreate()) {
resourceId = this.clientWritePlatformService.createClient(command);
} else if (entity.isUpdate()) {
EntityIdentifier result = this.clientWritePlatformService.updateClientDetails(command);
resourceId = result.getEntityId();
} else if (entity.isDelete()) {
EntityIdentifier result = this.clientWritePlatformService.deleteClient(command);
resourceId = result.getEntityId();
}
}
final CommandSource commandSourceResult = this.commandSourceHandlerDelegator.handleExistingCommand(commandSourceInput);
entity.updateResourceId(resourceId);
commandSourceRepository.save(commandSourceResult);
commandSourceRepository.save(entity);
return EntityIdentifier.makerChecker(resourceId, entity.getId());
return EntityIdentifier.makerChecker(commandSourceResult.resourceId(), commandSourceResult.getId());
}
@Transactional