This is an automated email from the ASF dual-hosted git repository. myrle pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/fineract-cn-customer.git
commit 5e05d4a57244184ef3b92757dc79b975cc489dad Author: Myrle Krantz <my...@apache.org> AuthorDate: Fri Oct 20 12:45:17 2017 +0200 TestCustomer.shouldDeletePortrait was failing intermittently with an InternalServerError resulting from a NullPointerException in the GET endpoint implementation. The delete transaction was getting committed between the query if the portrait exits and the actually acquisition of the portrait in the rest implementation. I changed the repository to return an Optional.empty instead of a null if the identifier doesn't correspond to a customer. This enabled me to replace two calls with one and eliminate the potential for a race condition. This in turn probably eliminated several other potential NullPointerExceptions. --- .../main/java/io/mifos/customer/TestCustomer.java | 6 +- .../command/handler/CustomerAggregate.java | 87 +++------ .../command/handler/DocumentCommandHandler.java | 18 +- .../internal/command/handler/TaskAggregate.java | 12 +- .../internal/repository/CommandRepository.java | 4 +- .../internal/repository/CustomerRepository.java | 4 +- .../repository/IdentificationCardRepository.java | 4 +- .../service/internal/service/CustomerService.java | 213 ++++++++------------- .../service/internal/service/TaskService.java | 7 +- .../rest/controller/CustomerRestController.java | 21 +- 10 files changed, 148 insertions(+), 228 deletions(-) diff --git a/component-test/src/main/java/io/mifos/customer/TestCustomer.java b/component-test/src/main/java/io/mifos/customer/TestCustomer.java index d11d5a8..5ce3f66 100644 --- a/component-test/src/main/java/io/mifos/customer/TestCustomer.java +++ b/component-test/src/main/java/io/mifos/customer/TestCustomer.java @@ -411,17 +411,17 @@ public class TestCustomer extends AbstractCustomerTest { this.customerManager.createCustomer(customer); - this.eventRecorder.wait(CustomerEventConstants.POST_CUSTOMER, customer.getIdentifier()); + Assert.assertTrue(this.eventRecorder.wait(CustomerEventConstants.POST_CUSTOMER, customer.getIdentifier())); final MockMultipartFile firstFile = new MockMultipartFile("portrait", "test.png", MediaType.IMAGE_PNG_VALUE, "i don't care".getBytes()); this.customerManager.postPortrait(customer.getIdentifier(), firstFile); - this.eventRecorder.wait(CustomerEventConstants.POST_PORTRAIT, customer.getIdentifier()); + Assert.assertTrue(this.eventRecorder.wait(CustomerEventConstants.POST_PORTRAIT, customer.getIdentifier())); this.customerManager.deletePortrait(customer.getIdentifier()); - this.eventRecorder.wait(CustomerEventConstants.DELETE_PORTRAIT, customer.getIdentifier()); + Assert.assertTrue(this.eventRecorder.wait(CustomerEventConstants.DELETE_PORTRAIT, customer.getIdentifier())); this.customerManager.getPortrait(customer.getIdentifier()); } diff --git a/service/src/main/java/io/mifos/customer/service/internal/command/handler/CustomerAggregate.java b/service/src/main/java/io/mifos/customer/service/internal/command/handler/CustomerAggregate.java index fa306a5..07e5ebc 100644 --- a/service/src/main/java/io/mifos/customer/service/internal/command/handler/CustomerAggregate.java +++ b/service/src/main/java/io/mifos/customer/service/internal/command/handler/CustomerAggregate.java @@ -25,53 +25,11 @@ import io.mifos.customer.api.v1.CustomerEventConstants; import io.mifos.customer.api.v1.domain.Command; import io.mifos.customer.api.v1.domain.Customer; import io.mifos.customer.api.v1.events.ScanEvent; -import io.mifos.customer.catalog.service.internal.repository.CatalogEntity; -import io.mifos.customer.catalog.service.internal.repository.CatalogRepository; -import io.mifos.customer.catalog.service.internal.repository.FieldEntity; -import io.mifos.customer.catalog.service.internal.repository.FieldRepository; -import io.mifos.customer.catalog.service.internal.repository.FieldValueEntity; -import io.mifos.customer.catalog.service.internal.repository.FieldValueRepository; -import io.mifos.customer.service.ServiceConstants; -import io.mifos.customer.service.internal.command.ActivateCustomerCommand; -import io.mifos.customer.service.internal.command.CloseCustomerCommand; -import io.mifos.customer.service.internal.command.CreateCustomerCommand; -import io.mifos.customer.service.internal.command.CreateIdentificationCardCommand; -import io.mifos.customer.service.internal.command.CreateIdentificationCardScanCommand; -import io.mifos.customer.service.internal.command.CreatePortraitCommand; -import io.mifos.customer.service.internal.command.DeleteIdentificationCardCommand; -import io.mifos.customer.service.internal.command.DeleteIdentificationCardScanCommand; -import io.mifos.customer.service.internal.command.DeletePortraitCommand; -import io.mifos.customer.service.internal.command.LockCustomerCommand; -import io.mifos.customer.service.internal.command.ReopenCustomerCommand; -import io.mifos.customer.service.internal.command.UnlockCustomerCommand; -import io.mifos.customer.service.internal.command.UpdateAddressCommand; -import io.mifos.customer.service.internal.command.UpdateContactDetailsCommand; -import io.mifos.customer.service.internal.command.UpdateCustomerCommand; -import io.mifos.customer.service.internal.command.UpdateIdentificationCardCommand; -import io.mifos.customer.service.internal.mapper.AddressMapper; -import io.mifos.customer.service.internal.mapper.CommandMapper; -import io.mifos.customer.service.internal.mapper.ContactDetailMapper; -import io.mifos.customer.service.internal.mapper.CustomerMapper; -import io.mifos.customer.service.internal.mapper.FieldValueMapper; -import io.mifos.customer.service.internal.mapper.IdentificationCardMapper; -import io.mifos.customer.service.internal.mapper.IdentificationCardScanMapper; -import io.mifos.customer.service.internal.mapper.PortraitMapper; -import io.mifos.customer.service.internal.repository.AddressEntity; -import io.mifos.customer.service.internal.repository.AddressRepository; -import io.mifos.customer.service.internal.repository.CommandRepository; -import io.mifos.customer.service.internal.repository.ContactDetailEntity; -import io.mifos.customer.service.internal.repository.ContactDetailRepository; -import io.mifos.customer.service.internal.repository.CustomerEntity; -import io.mifos.customer.service.internal.repository.CustomerRepository; -import io.mifos.customer.service.internal.repository.IdentificationCardEntity; -import io.mifos.customer.service.internal.repository.IdentificationCardRepository; -import io.mifos.customer.service.internal.repository.IdentificationCardScanEntity; -import io.mifos.customer.service.internal.repository.IdentificationCardScanRepository; -import io.mifos.customer.service.internal.repository.PortraitEntity; -import io.mifos.customer.service.internal.repository.PortraitRepository; -import org.slf4j.Logger; +import io.mifos.customer.catalog.service.internal.repository.*; +import io.mifos.customer.service.internal.command.*; +import io.mifos.customer.service.internal.mapper.*; +import io.mifos.customer.service.internal.repository.*; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -84,11 +42,9 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -@SuppressWarnings("unused") +@SuppressWarnings({"unused", "UnusedReturnValue"}) @Aggregate public class CustomerAggregate { - - private final Logger logger; private final AddressRepository addressRepository; private final CustomerRepository customerRepository; private final IdentificationCardRepository identificationCardRepository; @@ -102,8 +58,7 @@ public class CustomerAggregate { private final TaskAggregate taskAggregate; @Autowired - public CustomerAggregate(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger, - final AddressRepository addressRepository, + public CustomerAggregate(final AddressRepository addressRepository, final CustomerRepository customerRepository, final IdentificationCardRepository identificationCardRepository, final IdentificationCardScanRepository identificationCardScanRepository, @@ -115,7 +70,6 @@ public class CustomerAggregate { final CommandRepository commandRepository, final TaskAggregate taskAggregate) { super(); - this.logger = logger; this.addressRepository = addressRepository; this.customerRepository = customerRepository; this.identificationCardRepository = identificationCardRepository; @@ -170,7 +124,7 @@ public class CustomerAggregate { public String updateCustomer(final UpdateCustomerCommand updateCustomerCommand) { final Customer customer = updateCustomerCommand.customer(); - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(customer.getIdentifier()); + final CustomerEntity customerEntity = findCustomerEntityOrThrow(customer.getIdentifier()); customerEntity.setGivenName(customer.getGivenName()); customerEntity.setMiddleName(customer.getMiddleName()); @@ -218,7 +172,7 @@ public class CustomerAggregate { @CommandHandler @EventEmitter(selectorName = CustomerEventConstants.SELECTOR_NAME, selectorValue = CustomerEventConstants.ACTIVATE_CUSTOMER) public String activateCustomer(final ActivateCustomerCommand activateCustomerCommand) { - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(activateCustomerCommand.identifier()); + final CustomerEntity customerEntity = findCustomerEntityOrThrow(activateCustomerCommand.identifier()); if (this.taskAggregate.openTasksForCustomerExist(customerEntity, Command.Action.ACTIVATE.name())) { throw ServiceException.conflict("Open Tasks for customer {0} exists.", activateCustomerCommand.identifier()); @@ -241,7 +195,8 @@ public class CustomerAggregate { @CommandHandler @EventEmitter(selectorName = CustomerEventConstants.SELECTOR_NAME, selectorValue = CustomerEventConstants.LOCK_CUSTOMER) public String lockCustomer(final LockCustomerCommand lockCustomerCommand) { - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(lockCustomerCommand.identifier()); + final CustomerEntity customerEntity = findCustomerEntityOrThrow(lockCustomerCommand.identifier()); + customerEntity.setCurrentState(Customer.State.LOCKED.name()); customerEntity.setLastModifiedBy(UserContextHolder.checkedGetUser()); customerEntity.setLastModifiedOn(LocalDateTime.now(Clock.systemUTC())); @@ -261,7 +216,7 @@ public class CustomerAggregate { @CommandHandler @EventEmitter(selectorName = CustomerEventConstants.SELECTOR_NAME, selectorValue = CustomerEventConstants.UNLOCK_CUSTOMER) public String unlockCustomer(final UnlockCustomerCommand unlockCustomerCommand) { - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(unlockCustomerCommand.identifier()); + final CustomerEntity customerEntity = findCustomerEntityOrThrow(unlockCustomerCommand.identifier()); if (this.taskAggregate.openTasksForCustomerExist(customerEntity, Command.Action.UNLOCK.name())) { throw ServiceException.conflict("Open Tasks for customer {0} exists.", unlockCustomerCommand.identifier()); @@ -284,7 +239,8 @@ public class CustomerAggregate { @CommandHandler @EventEmitter(selectorName = CustomerEventConstants.SELECTOR_NAME, selectorValue = CustomerEventConstants.CLOSE_CUSTOMER) public String closeCustomer(final CloseCustomerCommand closeCustomerCommand) { - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(closeCustomerCommand.identifier()); + final CustomerEntity customerEntity = findCustomerEntityOrThrow(closeCustomerCommand.identifier()); + customerEntity.setCurrentState(Customer.State.CLOSED.name()); customerEntity.setLastModifiedBy(UserContextHolder.checkedGetUser()); customerEntity.setLastModifiedOn(LocalDateTime.now(Clock.systemUTC())); @@ -304,7 +260,7 @@ public class CustomerAggregate { @CommandHandler @EventEmitter(selectorName = CustomerEventConstants.SELECTOR_NAME, selectorValue = CustomerEventConstants.REOPEN_CUSTOMER) public String reopenCustomer(final ReopenCustomerCommand reopenCustomerCommand) { - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(reopenCustomerCommand.identifier()); + final CustomerEntity customerEntity = findCustomerEntityOrThrow(reopenCustomerCommand.identifier()); if (this.taskAggregate.openTasksForCustomerExist(customerEntity, Command.Action.REOPEN.name())) { throw ServiceException.conflict("Open Tasks for customer {0} exists.", reopenCustomerCommand.identifier()); @@ -327,7 +283,7 @@ public class CustomerAggregate { @CommandHandler @EventEmitter(selectorName = CustomerEventConstants.SELECTOR_NAME, selectorValue = CustomerEventConstants.PUT_ADDRESS) public String updateAddress(final UpdateAddressCommand updateAddressCommand) { - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(updateAddressCommand.identifier()); + final CustomerEntity customerEntity = findCustomerEntityOrThrow(updateAddressCommand.identifier()); customerEntity.setLastModifiedBy(UserContextHolder.checkedGetUser()); customerEntity.setLastModifiedOn(LocalDateTime.now(Clock.systemUTC())); @@ -347,7 +303,7 @@ public class CustomerAggregate { @CommandHandler @EventEmitter(selectorName = CustomerEventConstants.SELECTOR_NAME, selectorValue = CustomerEventConstants.PUT_CONTACT_DETAILS) public String updateContactDetails(final UpdateContactDetailsCommand updateContactDetailsCommand) { - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(updateContactDetailsCommand.identifier()); + final CustomerEntity customerEntity = findCustomerEntityOrThrow(updateContactDetailsCommand.identifier()); customerEntity.setLastModifiedBy(UserContextHolder.checkedGetUser()); customerEntity.setLastModifiedOn(LocalDateTime.now(Clock.systemUTC())); @@ -374,7 +330,7 @@ public class CustomerAggregate { @CommandHandler @EventEmitter(selectorName = CustomerEventConstants.SELECTOR_NAME, selectorValue = CustomerEventConstants.POST_IDENTIFICATION_CARD) public String createIdentificationCard(final CreateIdentificationCardCommand createIdentificationCardCommand) { - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(createIdentificationCardCommand.identifier()); + final CustomerEntity customerEntity = findCustomerEntityOrThrow(createIdentificationCardCommand.identifier()); final IdentificationCardEntity identificationCardEntity = IdentificationCardMapper.map(createIdentificationCardCommand.identificationCard()); @@ -507,7 +463,7 @@ public class CustomerAggregate { return null; } - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(createPortraitCommand.identifier()); + final CustomerEntity customerEntity = findCustomerEntityOrThrow(createPortraitCommand.identifier()); final PortraitEntity portraitEntity = PortraitMapper.map(createPortraitCommand.portrait()); portraitEntity.setCustomer(customerEntity); @@ -525,7 +481,7 @@ public class CustomerAggregate { @CommandHandler @EventEmitter(selectorName = CustomerEventConstants.SELECTOR_NAME, selectorValue = CustomerEventConstants.DELETE_PORTRAIT) public String deletePortrait(final DeletePortraitCommand deletePortraitCommand) throws IOException { - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(deletePortraitCommand.identifier()); + final CustomerEntity customerEntity = findCustomerEntityOrThrow(deletePortraitCommand.identifier()); this.portraitRepository.deleteByCustomer(customerEntity); @@ -557,4 +513,9 @@ public class CustomerAggregate { .collect(Collectors.toList()) ); } + + private CustomerEntity findCustomerEntityOrThrow(String identifier) { + return this.customerRepository.findByIdentifier(identifier) + .orElseThrow(() -> ServiceException.notFound("Customer ''{0}'' not found", identifier)); + } } diff --git a/service/src/main/java/io/mifos/customer/service/internal/command/handler/DocumentCommandHandler.java b/service/src/main/java/io/mifos/customer/service/internal/command/handler/DocumentCommandHandler.java index 9c27aab..c91fcc0 100644 --- a/service/src/main/java/io/mifos/customer/service/internal/command/handler/DocumentCommandHandler.java +++ b/service/src/main/java/io/mifos/customer/service/internal/command/handler/DocumentCommandHandler.java @@ -71,11 +71,9 @@ public class DocumentCommandHandler { @CommandHandler @EventEmitter(selectorName = CustomerEventConstants.SELECTOR_NAME, selectorValue = CustomerEventConstants.POST_DOCUMENT) public DocumentEvent process(final CreateDocumentCommand command) throws IOException { - - - final CustomerEntity customerEntity = customerRepository.findByIdentifier(command.getCustomerIdentifier()); - final DocumentEntity documentEntity = DocumentMapper.map(command.getCustomerDocument(), customerEntity); - documentRepository.save(documentEntity); + customerRepository.findByIdentifier(command.getCustomerIdentifier()) + .map(customerEntity -> DocumentMapper.map(command.getCustomerDocument(), customerEntity)) + .ifPresent(documentRepository::save); return new DocumentEvent(command.getCustomerIdentifier(), command.getCustomerDocument().getIdentifier()); } @@ -90,10 +88,12 @@ public class DocumentCommandHandler { ServiceException.notFound("Document ''{0}'' for customer ''{1}'' not found", command.getCustomerDocument().getIdentifier(), command.getCustomerIdentifier())); - final CustomerEntity customerEntity = customerRepository.findByIdentifier(command.getCustomerIdentifier()); - final DocumentEntity documentEntity = DocumentMapper.map(command.getCustomerDocument(), customerEntity); - documentEntity.setId(existingDocument.getId()); - documentRepository.save(documentEntity); + customerRepository.findByIdentifier(command.getCustomerIdentifier()) + .map(customerEntity -> DocumentMapper.map(command.getCustomerDocument(), customerEntity)) + .ifPresent(documentEntity -> { + documentEntity.setId(existingDocument.getId()); + documentRepository.save(documentEntity); + }); return new DocumentEvent(command.getCustomerIdentifier(), command.getCustomerDocument().getIdentifier()); } diff --git a/service/src/main/java/io/mifos/customer/service/internal/command/handler/TaskAggregate.java b/service/src/main/java/io/mifos/customer/service/internal/command/handler/TaskAggregate.java index 5e1bac6..ab4cb41 100644 --- a/service/src/main/java/io/mifos/customer/service/internal/command/handler/TaskAggregate.java +++ b/service/src/main/java/io/mifos/customer/service/internal/command/handler/TaskAggregate.java @@ -19,6 +19,7 @@ import io.mifos.core.api.util.UserContextHolder; import io.mifos.core.command.annotation.Aggregate; import io.mifos.core.command.annotation.CommandHandler; import io.mifos.core.command.annotation.EventEmitter; +import io.mifos.core.lang.ServiceException; import io.mifos.customer.api.v1.CustomerEventConstants; import io.mifos.customer.api.v1.domain.Command; import io.mifos.customer.api.v1.domain.TaskDefinition; @@ -95,8 +96,7 @@ public class TaskAggregate { final TaskDefinitionEntity taskDefinitionEntity = this.taskDefinitionRepository.findByIdentifier(addTaskDefinitionToCustomerCommand.taskIdentifier()); - final CustomerEntity customerEntity = - this.customerRepository.findByIdentifier(addTaskDefinitionToCustomerCommand.customerIdentifier()); + final CustomerEntity customerEntity = findCustomerEntityOrThrow(addTaskDefinitionToCustomerCommand.customerIdentifier()); this.taskInstanceRepository.save(TaskInstanceMapper.create(taskDefinitionEntity, customerEntity)); @@ -107,8 +107,7 @@ public class TaskAggregate { @CommandHandler @EventEmitter(selectorName = CustomerEventConstants.SELECTOR_NAME, selectorValue = CustomerEventConstants.PUT_CUSTOMER) public String executeTaskForCustomer(final ExecuteTaskForCustomerCommand executeTaskForCustomerCommand) { - final CustomerEntity customerEntity = - this.customerRepository.findByIdentifier(executeTaskForCustomerCommand.customerIdentifier()); + final CustomerEntity customerEntity = findCustomerEntityOrThrow(executeTaskForCustomerCommand.customerIdentifier()); final List<TaskInstanceEntity> taskInstanceEntities = this.taskInstanceRepository.findByCustomer(customerEntity); if (taskInstanceEntities != null) { final Optional<TaskInstanceEntity> taskInstanceEntityOptional = taskInstanceEntities @@ -160,4 +159,9 @@ public class TaskAggregate { return false; } } + + private CustomerEntity findCustomerEntityOrThrow(String identifier) { + return this.customerRepository.findByIdentifier(identifier) + .orElseThrow(() -> ServiceException.notFound("Customer ''{0}'' not found", identifier)); + } } diff --git a/service/src/main/java/io/mifos/customer/service/internal/repository/CommandRepository.java b/service/src/main/java/io/mifos/customer/service/internal/repository/CommandRepository.java index 7d0be4f..1d69ea9 100644 --- a/service/src/main/java/io/mifos/customer/service/internal/repository/CommandRepository.java +++ b/service/src/main/java/io/mifos/customer/service/internal/repository/CommandRepository.java @@ -17,9 +17,9 @@ package io.mifos.customer.service.internal.repository; import org.springframework.data.jpa.repository.JpaRepository; -import java.util.List; +import java.util.stream.Stream; public interface CommandRepository extends JpaRepository<CommandEntity, Long> { - List<CommandEntity> findByCustomer(final CustomerEntity customerEntity); + Stream<CommandEntity> findByCustomer(final CustomerEntity customerEntity); } diff --git a/service/src/main/java/io/mifos/customer/service/internal/repository/CustomerRepository.java b/service/src/main/java/io/mifos/customer/service/internal/repository/CustomerRepository.java index 0a85fa9..61391ad 100644 --- a/service/src/main/java/io/mifos/customer/service/internal/repository/CustomerRepository.java +++ b/service/src/main/java/io/mifos/customer/service/internal/repository/CustomerRepository.java @@ -22,6 +22,8 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.util.Optional; + @Repository public interface CustomerRepository extends JpaRepository<CustomerEntity, Long> { @@ -31,7 +33,7 @@ public interface CustomerRepository extends JpaRepository<CustomerEntity, Long> Page<CustomerEntity> findByIdentifierContainingOrGivenNameContainingOrSurnameContaining( final String identifier, final String givenName, final String surname, final Pageable pageable); - CustomerEntity findByIdentifier(final String identifier); + Optional<CustomerEntity> findByIdentifier(final String identifier); Page<CustomerEntity> findByCurrentStateNot(final String state, final Pageable pageable); diff --git a/service/src/main/java/io/mifos/customer/service/internal/repository/IdentificationCardRepository.java b/service/src/main/java/io/mifos/customer/service/internal/repository/IdentificationCardRepository.java index 294c249..7903e82 100644 --- a/service/src/main/java/io/mifos/customer/service/internal/repository/IdentificationCardRepository.java +++ b/service/src/main/java/io/mifos/customer/service/internal/repository/IdentificationCardRepository.java @@ -20,8 +20,8 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -import java.util.List; import java.util.Optional; +import java.util.stream.Stream; @Repository public interface IdentificationCardRepository extends JpaRepository<IdentificationCardEntity, Long> { @@ -31,5 +31,5 @@ public interface IdentificationCardRepository extends JpaRepository<Identificati Optional<IdentificationCardEntity> findByNumber(final String number); - List<IdentificationCardEntity> findByCustomer(final CustomerEntity customerEntity); + Stream<IdentificationCardEntity> findByCustomer(final CustomerEntity customerEntity); } diff --git a/service/src/main/java/io/mifos/customer/service/internal/service/CustomerService.java b/service/src/main/java/io/mifos/customer/service/internal/service/CustomerService.java index cc886aa..280b509 100644 --- a/service/src/main/java/io/mifos/customer/service/internal/service/CustomerService.java +++ b/service/src/main/java/io/mifos/customer/service/internal/service/CustomerService.java @@ -15,42 +15,14 @@ */ package io.mifos.customer.service.internal.service; -import io.mifos.customer.api.v1.domain.Command; -import io.mifos.customer.api.v1.domain.Customer; -import io.mifos.customer.api.v1.domain.CustomerPage; -import io.mifos.customer.api.v1.domain.IdentificationCard; -import io.mifos.customer.api.v1.domain.IdentificationCardScan; -import io.mifos.customer.api.v1.domain.ProcessStep; -import io.mifos.customer.api.v1.domain.TaskDefinition; +import io.mifos.customer.api.v1.domain.*; import io.mifos.customer.catalog.api.v1.domain.Value; import io.mifos.customer.catalog.service.internal.repository.FieldEntity; import io.mifos.customer.catalog.service.internal.repository.FieldValueEntity; import io.mifos.customer.catalog.service.internal.repository.FieldValueRepository; -import io.mifos.customer.service.ServiceConstants; -import io.mifos.customer.service.internal.mapper.AddressMapper; -import io.mifos.customer.service.internal.mapper.CommandMapper; -import io.mifos.customer.service.internal.mapper.ContactDetailMapper; -import io.mifos.customer.service.internal.mapper.CustomerMapper; -import io.mifos.customer.service.internal.mapper.IdentificationCardMapper; -import io.mifos.customer.service.internal.mapper.IdentificationCardScanMapper; -import io.mifos.customer.service.internal.mapper.TaskDefinitionMapper; -import io.mifos.customer.service.internal.repository.CommandEntity; -import io.mifos.customer.service.internal.repository.CommandRepository; -import io.mifos.customer.service.internal.repository.ContactDetailEntity; -import io.mifos.customer.service.internal.repository.ContactDetailRepository; -import io.mifos.customer.service.internal.repository.CustomerEntity; -import io.mifos.customer.service.internal.repository.CustomerRepository; -import io.mifos.customer.service.internal.repository.IdentificationCardEntity; -import io.mifos.customer.service.internal.repository.IdentificationCardRepository; -import io.mifos.customer.service.internal.repository.IdentificationCardScanEntity; -import io.mifos.customer.service.internal.repository.IdentificationCardScanRepository; -import io.mifos.customer.service.internal.repository.PortraitEntity; -import io.mifos.customer.service.internal.repository.PortraitRepository; -import io.mifos.customer.service.internal.repository.TaskDefinitionRepository; -import io.mifos.customer.service.internal.repository.TaskInstanceRepository; -import org.slf4j.Logger; +import io.mifos.customer.service.internal.mapper.*; +import io.mifos.customer.service.internal.repository.*; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -60,11 +32,11 @@ import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import java.util.stream.Stream; @Service public class CustomerService { - private final Logger logger; private final CustomerRepository customerRepository; private final IdentificationCardRepository identificationCardRepository; private final IdentificationCardScanRepository identificationCardScanRepository; @@ -76,8 +48,7 @@ public class CustomerService { private final TaskInstanceRepository taskInstanceRepository; @Autowired - public CustomerService(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger, - final CustomerRepository customerRepository, + public CustomerService(final CustomerRepository customerRepository, final IdentificationCardRepository identificationCardRepository, final IdentificationCardScanRepository identificationCardScanRepository, final PortraitRepository portraitRepository, @@ -87,7 +58,6 @@ public class CustomerService { final TaskDefinitionRepository taskDefinitionRepository, final TaskInstanceRepository taskInstanceRepository) { super(); - this.logger = logger; this.customerRepository = customerRepository; this.identificationCardRepository = identificationCardRepository; this.identificationCardScanRepository = identificationCardScanRepository; @@ -103,10 +73,6 @@ public class CustomerService { return this.customerRepository.existsByIdentifier(identifier); } - public Boolean portraitExists(final String identifier) { - return this.portraitRepository.existsByIdentifier(identifier); - } - public Boolean identificationCardExists(final String number) { return this.identificationCardRepository.existsByNumber(number); } @@ -118,41 +84,39 @@ public class CustomerService { } public Optional<Customer> findCustomer(final String identifier) { - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(identifier); - if (customerEntity != null) { - final Customer customer = CustomerMapper.map(customerEntity); - customer.setAddress(AddressMapper.map(customerEntity.getAddress())); - - final List<ContactDetailEntity> contactDetailEntities = this.contactDetailRepository.findByCustomer(customerEntity); - if (contactDetailEntities != null) { - customer.setContactDetails( - contactDetailEntities - .stream() - .map(ContactDetailMapper::map) - .collect(Collectors.toList()) - ); - } - - final List<FieldValueEntity> fieldValueEntities = this.fieldValueRepository.findByCustomer(customerEntity); - if (fieldValueEntities != null) { - customer.setCustomValues( - fieldValueEntities - .stream() - .map(fieldValueEntity -> { - final Value value = new Value(); - value.setValue(fieldValueEntity.getValue()); - final FieldEntity fieldEntity = fieldValueEntity.getField(); - value.setCatalogIdentifier(fieldEntity.getCatalog().getIdentifier()); - value.setFieldIdentifier(fieldEntity.getIdentifier()); - return value; - }).collect(Collectors.toList()) - ); - } - - return Optional.of(customer); - } else { - return Optional.empty(); - } + return customerRepository.findByIdentifier(identifier) + .map(customerEntity -> { + final Customer customer = CustomerMapper.map(customerEntity); + customer.setAddress(AddressMapper.map(customerEntity.getAddress())); + + final List<ContactDetailEntity> contactDetailEntities = this.contactDetailRepository.findByCustomer(customerEntity); + if (contactDetailEntities != null) { + customer.setContactDetails( + contactDetailEntities + .stream() + .map(ContactDetailMapper::map) + .collect(Collectors.toList()) + ); + } + + final List<FieldValueEntity> fieldValueEntities = this.fieldValueRepository.findByCustomer(customerEntity); + if (fieldValueEntities != null) { + customer.setCustomValues( + fieldValueEntities + .stream() + .map(fieldValueEntity -> { + final Value value = new Value(); + value.setValue(fieldValueEntity.getValue()); + final FieldEntity fieldEntity = fieldValueEntity.getField(); + value.setCatalogIdentifier(fieldEntity.getCatalog().getIdentifier()); + value.setFieldIdentifier(fieldEntity.getIdentifier()); + return value; + }).collect(Collectors.toList()) + ); + } + + return customer; + }); } public CustomerPage fetchCustomer(final String term, final Boolean includeClosed, final Pageable pageable) { @@ -186,31 +150,23 @@ public class CustomerService { return customerPage; } - public final List<Command> fetchCommandsByCustomer(final String identifier) { - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(identifier); - final List<CommandEntity> commands = this.commandRepository.findByCustomer(customerEntity); - if (commands != null) { - return commands.stream().map(CommandMapper::map).collect(Collectors.toList()); - } else { - return Collections.emptyList(); - } + public final Stream<Command> fetchCommandsByCustomer(final String identifier) { + return customerRepository.findByIdentifier(identifier) + .map(commandRepository::findByCustomer) + .orElse(Stream.empty()) + .map(CommandMapper::map); } - public final PortraitEntity findPortrait(final String identifier) { - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(identifier); - - return this.portraitRepository.findByCustomer(customerEntity); + public final Optional<PortraitEntity> findPortrait(final String identifier) { + return customerRepository.findByIdentifier(identifier) + .map(portraitRepository::findByCustomer); } - public final List<IdentificationCard> fetchIdentificationCardsByCustomer(final String identifier) { - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(identifier); - final List<IdentificationCardEntity> identificationCards = this.identificationCardRepository.findByCustomer(customerEntity); - - if (identificationCards != null) { - return identificationCards.stream().map(IdentificationCardMapper::map).collect(Collectors.toList()); - } else { - return Collections.emptyList(); - } + public final Stream<IdentificationCard> fetchIdentificationCardsByCustomer(final String identifier) { + return customerRepository.findByIdentifier(identifier) + .map(identificationCardRepository::findByCustomer) + .orElse(Stream.empty()) + .map(IdentificationCardMapper::map); } public Optional<IdentificationCard> findIdentificationCard(final String number) { @@ -229,8 +185,7 @@ public class CustomerService { private Optional<IdentificationCardScanEntity> findIdentificationCardEntity(final String number, final String identifier) { final Optional<IdentificationCardEntity> cardEntity = this.identificationCardRepository.findByNumber(number); - final Optional<IdentificationCardScanEntity> cardScanEntity = cardEntity.flatMap(card -> this.identificationCardScanRepository.findByIdentifierAndIdentificationCard(identifier, card)); - return cardScanEntity; + return cardEntity.flatMap(card -> this.identificationCardScanRepository.findByIdentifierAndIdentificationCard(identifier, card)); } public Optional<IdentificationCardScan> findIdentificationCardScan(final String number, final String identifier) { @@ -242,29 +197,32 @@ public class CustomerService { } public List<ProcessStep> getProcessSteps(final String customerIdentifier) { - final ArrayList<ProcessStep> processSteps = new ArrayList<>(); - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(customerIdentifier); - - final Customer.State state = Customer.State.valueOf(customerEntity.getCurrentState()); - switch (state) { - case PENDING: - processSteps.add(this.buildProcessStep(customerEntity, Command.Action.ACTIVATE)); - processSteps.add(this.buildProcessStep(customerEntity, Command.Action.CLOSE)); - break; - case ACTIVE: - processSteps.add(this.buildProcessStep(customerEntity, Command.Action.LOCK)); - processSteps.add(this.buildProcessStep(customerEntity, Command.Action.CLOSE)); - break; - case LOCKED: - processSteps.add(this.buildProcessStep(customerEntity, Command.Action.UNLOCK)); - processSteps.add(this.buildProcessStep(customerEntity, Command.Action.CLOSE)); - break; - case CLOSED: - processSteps.add(this.buildProcessStep(customerEntity, Command.Action.REOPEN)); - break; - } - - return processSteps; + return customerRepository.findByIdentifier(customerIdentifier) + .map(customerEntity -> { + final List<ProcessStep> processSteps = new ArrayList<>(); + + final Customer.State state = Customer.State.valueOf(customerEntity.getCurrentState()); + switch (state) { + case PENDING: + processSteps.add(this.buildProcessStep(customerEntity, Command.Action.ACTIVATE)); + processSteps.add(this.buildProcessStep(customerEntity, Command.Action.CLOSE)); + break; + case ACTIVE: + processSteps.add(this.buildProcessStep(customerEntity, Command.Action.LOCK)); + processSteps.add(this.buildProcessStep(customerEntity, Command.Action.CLOSE)); + break; + case LOCKED: + processSteps.add(this.buildProcessStep(customerEntity, Command.Action.UNLOCK)); + processSteps.add(this.buildProcessStep(customerEntity, Command.Action.CLOSE)); + break; + case CLOSED: + processSteps.add(this.buildProcessStep(customerEntity, Command.Action.REOPEN)); + break; + } + + return processSteps; + }) + .orElse(Collections.emptyList()); } private ProcessStep buildProcessStep(final CustomerEntity customerEntity, final Command.Action action) { @@ -276,14 +234,13 @@ public class CustomerService { final ArrayList<TaskDefinition> taskDefinitions = new ArrayList<>(); this.taskDefinitionRepository.findByAssignedCommandsContaining(action.name()) - .forEach(taskDefinitionEntity -> { - this.taskInstanceRepository.findByCustomerAndTaskDefinition(customerEntity, taskDefinitionEntity) - .forEach(taskInstanceEntity -> { - if (taskInstanceEntity.getExecutedBy() == null) { - taskDefinitions.add(TaskDefinitionMapper.map(taskDefinitionEntity)); - } - }); - }); + .forEach(taskDefinitionEntity -> + this.taskInstanceRepository.findByCustomerAndTaskDefinition(customerEntity, taskDefinitionEntity) + .forEach(taskInstanceEntity -> { + if (taskInstanceEntity.getExecutedBy() == null) { + taskDefinitions.add(TaskDefinitionMapper.map(taskDefinitionEntity)); + } + })); processStep.setTaskDefinitions(taskDefinitions); return processStep; diff --git a/service/src/main/java/io/mifos/customer/service/internal/service/TaskService.java b/service/src/main/java/io/mifos/customer/service/internal/service/TaskService.java index 66132fc..a751601 100644 --- a/service/src/main/java/io/mifos/customer/service/internal/service/TaskService.java +++ b/service/src/main/java/io/mifos/customer/service/internal/service/TaskService.java @@ -17,7 +17,6 @@ package io.mifos.customer.service.internal.service; import io.mifos.customer.api.v1.domain.TaskDefinition; import io.mifos.customer.service.internal.mapper.TaskDefinitionMapper; -import io.mifos.customer.service.internal.repository.CustomerEntity; import io.mifos.customer.service.internal.repository.CustomerRepository; import io.mifos.customer.service.internal.repository.TaskDefinitionEntity; import io.mifos.customer.service.internal.repository.TaskDefinitionRepository; @@ -25,6 +24,7 @@ import io.mifos.customer.service.internal.repository.TaskInstanceRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -67,8 +67,9 @@ public class TaskService { } public List<TaskDefinition> findTasksByCustomer(final String customerIdentifier, Boolean includeExecuted) { - final CustomerEntity customerEntity = this.customerRepository.findByIdentifier(customerIdentifier); - return this.taskInstanceRepository.findByCustomer(customerEntity) + return customerRepository.findByIdentifier(customerIdentifier) + .map(taskInstanceRepository::findByCustomer) + .orElse(Collections.emptyList()) .stream() .filter(taskInstanceEntity -> (includeExecuted ? true : taskInstanceEntity.getExecutedBy() == null)) .map(taskInstanceEntity -> TaskDefinitionMapper.map(taskInstanceEntity.getTaskDefinition())) diff --git a/service/src/main/java/io/mifos/customer/service/rest/controller/CustomerRestController.java b/service/src/main/java/io/mifos/customer/service/rest/controller/CustomerRestController.java index d07124e..d5010f5 100644 --- a/service/src/main/java/io/mifos/customer/service/rest/controller/CustomerRestController.java +++ b/service/src/main/java/io/mifos/customer/service/rest/controller/CustomerRestController.java @@ -79,6 +79,8 @@ import javax.validation.Valid; import javax.validation.constraints.Size; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; @RestController @RequestMapping("/") @@ -267,7 +269,7 @@ public class CustomerRestController { @ResponseBody ResponseEntity<List<Command>> fetchCustomerCommands(@PathVariable("identifier") final String identifier) { if (this.customerService.customerExists(identifier)) { - return ResponseEntity.ok(this.customerService.fetchCommandsByCustomer(identifier)); + return ResponseEntity.ok(this.customerService.fetchCommandsByCustomer(identifier).collect(Collectors.toList())); } else { throw ServiceException.notFound("Customer {0} not found.", identifier); } @@ -315,8 +317,8 @@ public class CustomerRestController { final TaskDefinition taskDefinition = optionalTaskDefinition.get(); switch (TaskDefinition.Type.valueOf(taskDefinition.getType())) { case ID_CARD: - final List<IdentificationCard> identificationCards = this.customerService.fetchIdentificationCardsByCustomer(identifier); - if (identificationCards.isEmpty()) { + final Stream<IdentificationCard> identificationCards = this.customerService.fetchIdentificationCardsByCustomer(identifier); + if (!identificationCards.findAny().isPresent()) { throw ServiceException.conflict("No identification cards for customer found."); } break; @@ -401,7 +403,7 @@ public class CustomerRestController { ) public @ResponseBody ResponseEntity<List<IdentificationCard>> fetchIdentificationCards(@PathVariable("identifier") final String identifier) { this.throwIfCustomerNotExists(identifier); - return ResponseEntity.ok(this.customerService.fetchIdentificationCardsByCustomer(identifier)); + return ResponseEntity.ok(this.customerService.fetchIdentificationCardsByCustomer(identifier).collect(Collectors.toList())); } @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.IDENTIFICATIONS) @@ -612,9 +614,8 @@ public class CustomerRestController { consumes = MediaType.ALL_VALUE ) public ResponseEntity<byte[]> getPortrait(@PathVariable("identifier") final String identifier) { - this.throwIfPortraitNotExists(identifier); - - final PortraitEntity portrait = this.customerService.findPortrait(identifier); + final PortraitEntity portrait = this.customerService.findPortrait(identifier) + .orElseThrow(() -> ServiceException.notFound("Portrait for Customer ''{0}'' not found.", identifier)); return ResponseEntity .ok() @@ -759,12 +760,6 @@ public class CustomerRestController { } } - private void throwIfPortraitNotExists(final String identifier) { - if (!this.customerService.portraitExists(identifier)) { - throw ServiceException.notFound("Portrait for Customer {0} not found.", identifier); - } - } - private void throwIfIdentificationCardNotExists(final String number) { if (!this.customerService.identificationCardExists(number)) { throw ServiceException.notFound("Identification card {0} not found.", number); -- To stop receiving notification emails like this one, please contact my...@apache.org.