From 204d26fd7b54b04257c8903e6ac3374c784576c3 Mon Sep 17 00:00:00 2001 From: Jamie Briggs Date: Mon, 20 Jan 2025 16:31:38 +0000 Subject: [PATCH 01/12] CCMSPUI-381 Implement ebs-api get client transaction status endpoint Signed-off-by: Jamie Briggs --- data-api/open-api-specification.yml | 39 ++++++++++++++ .../data/controller/ClientsController.java | 23 ++++++++ .../ccms/data/entity/TransactionStatus.java | 52 +++++++++++++++++++ .../data/mapper/TransactionStatusMapper.java | 19 +++++++ .../repository/NotificationRepository.java | 2 +- .../TransactionStatusRepository.java | 13 +++++ .../ccms/data/service/TransactionService.java | 25 +++++++++ 7 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 data-service/src/main/java/uk/gov/laa/ccms/data/controller/ClientsController.java create mode 100644 data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java create mode 100644 data-service/src/main/java/uk/gov/laa/ccms/data/mapper/TransactionStatusMapper.java create mode 100644 data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java create mode 100644 data-service/src/main/java/uk/gov/laa/ccms/data/service/TransactionService.java diff --git a/data-api/open-api-specification.yml b/data-api/open-api-specification.yml index 863c668f..83ee277a 100644 --- a/data-api/open-api-specification.yml +++ b/data-api/open-api-specification.yml @@ -974,6 +974,36 @@ paths: description: 'Forbidden' '500': description: 'Internal server error' + /clients/status/{transaction-request-id}: + get: + tags: + - clients + summary: 'Get Client transaction status' + operationId: 'getClientTransactionStatus' + parameters: + - name: 'transaction-request-id' + in: 'path' + required: true + schema: + type: 'string' + example: 'abc123' + responses: + '200': + description: 'Successful operation' + content: + application/json: + schema: + $ref: "#/components/schemas/transactionStatus" + '400': + description: 'Bad request' + '401': + description: 'Unauthorized' + '403': + description: 'Forbidden' + '404': + description: 'Not found' + '500': + description: 'Internal server error' /notifications: get: tags: @@ -1054,6 +1084,15 @@ components: in: header name: Authorization schemas: + transactionStatus: + type: 'object' + properties: + submission_status: + type: 'string' + example: 'success' + reference_number: + type: 'string' + example: 'abc123' baseOffice: type: 'object' properties: diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/controller/ClientsController.java b/data-service/src/main/java/uk/gov/laa/ccms/data/controller/ClientsController.java new file mode 100644 index 00000000..b2e375e6 --- /dev/null +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/controller/ClientsController.java @@ -0,0 +1,23 @@ +package uk.gov.laa.ccms.data.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RestController; +import uk.gov.laa.ccms.data.api.ClientsApi; +import uk.gov.laa.ccms.data.model.TransactionStatus; +import uk.gov.laa.ccms.data.service.TransactionService; + +@RestController +public class ClientsController implements ClientsApi { + + private final TransactionService transactionService; + + public ClientsController(TransactionService transactionService) { + this.transactionService = transactionService; + } + + @Override + public ResponseEntity getClientTransactionStatus(String transactionRequestId) { + return transactionService.getTransactionStatus(transactionRequestId).map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } +} diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java new file mode 100644 index 00000000..ab797cc2 --- /dev/null +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java @@ -0,0 +1,52 @@ +package uk.gov.laa.ccms.data.entity; + + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import java.sql.Date; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * Represents a transaction status entity from the "XXCCMS_TRANSACTION_STATUS_V" database view. + * + *

This entity captures details about transaction statuses, such as the request ID, + * process name, record reference key, status, error description, and transaction + * occurrence date.

+ * + *

This class is immutable, and its instances can be created using the builder pattern.

+ */ +@Entity +@Table(name = "XXCCMS_TRANSACTION_STATUS_V") +@Getter +@Builder +@AllArgsConstructor +@RequiredArgsConstructor +public class TransactionStatus { + + @Id + @Column(name = "REQUEST_ID") + private String requestId; + + @Column(name = "PROCESS_NAME", length = 50) + private String processName; + + @Column(name = "RECORD_REF_KEY", length = 100) + private String recordRefKey; + + @Column(name = "RECORD_REF_VALUE", length = 250) + private String recordRefValue; + + @Column(name = "STATUS", length = 10) + private String status; + + @Column(name = "ERROR_DESCRIPTION", length = 2000) + private String errorDescription; + + @Column(name = "TRANSACTION_OCCURRENCE_DATE") + private Date transactionOccurrenceDate; +} diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/mapper/TransactionStatusMapper.java b/data-service/src/main/java/uk/gov/laa/ccms/data/mapper/TransactionStatusMapper.java new file mode 100644 index 00000000..db54e804 --- /dev/null +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/mapper/TransactionStatusMapper.java @@ -0,0 +1,19 @@ +package uk.gov.laa.ccms.data.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import uk.gov.laa.ccms.data.model.TransactionStatus; + +@Mapper(componentModel = "spring") +public interface TransactionStatusMapper { + + @Mapping(target = "submissionStatus", source = "status", qualifiedByName = "toUpperCase") + @Mapping(target = "referenceNumber", source = "recordRefValue") + TransactionStatus toTransactionStatus(uk.gov.laa.ccms.data.entity.TransactionStatus entity); + + @Named("toUpperCase") + static String toUpperCase(String value) { + return value == null ? null : value.toUpperCase(); + } +} diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/repository/NotificationRepository.java b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/NotificationRepository.java index 06c342b3..af69ff94 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/repository/NotificationRepository.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/NotificationRepository.java @@ -20,7 +20,7 @@ * @see org.springframework.data.jpa.domain.Specification */ @Repository -public interface NotificationRepository extends ReadOnlyRepository, +public interface NotificationRepository extends ReadOnlyRepository, JpaSpecificationExecutor { } diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java new file mode 100644 index 00000000..fd217f8c --- /dev/null +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java @@ -0,0 +1,13 @@ +package uk.gov.laa.ccms.data.repository; + +import java.util.Optional; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import uk.gov.laa.ccms.data.entity.TransactionStatus; + +@Repository +public interface TransactionStatusRepository extends ReadOnlyRepository { + + @Query("SELECT ts FROM TransactionStatus ts WHERE ts.requestId = ?1 AND ts.processName = 'CreateClient'") + Optional findClientTransactionByTransactionId(String transactionId); +} diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/service/TransactionService.java b/data-service/src/main/java/uk/gov/laa/ccms/data/service/TransactionService.java new file mode 100644 index 00000000..074dfc6c --- /dev/null +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/service/TransactionService.java @@ -0,0 +1,25 @@ +package uk.gov.laa.ccms.data.service; + +import java.util.Optional; +import org.springframework.stereotype.Service; +import uk.gov.laa.ccms.data.mapper.TransactionStatusMapper; +import uk.gov.laa.ccms.data.model.TransactionStatus; +import uk.gov.laa.ccms.data.repository.TransactionStatusRepository; + +@Service +public class TransactionService { + + private final TransactionStatusMapper transactionStatusMapper; + private final TransactionStatusRepository transactionStatusRepository; + + public TransactionService(TransactionStatusMapper transactionStatusMapper, + TransactionStatusRepository transactionStatusRepository) { + this.transactionStatusMapper = transactionStatusMapper; + this.transactionStatusRepository = transactionStatusRepository; + } + + public Optional getTransactionStatus(String transactionId) { + return transactionStatusRepository.findClientTransactionByTransactionId(transactionId).map( + transactionStatusMapper::toTransactionStatus); + } +} From 39dc1367e2d53889349aa9bcf78c6441f3b062d3 Mon Sep 17 00:00:00 2001 From: Jamie Briggs Date: Tue, 21 Jan 2025 12:27:01 +0000 Subject: [PATCH 02/12] CCMSPUI-381 Checkstyle issues resolved Signed-off-by: Jamie Briggs --- .../data/controller/ClientsController.java | 25 +++++++++-- .../controller/NotificationsController.java | 8 +++- .../data/mapper/TransactionStatusMapper.java | 22 +++++++++ .../TransactionStatusRepository.java | 23 +++++++++- .../laa/ccms/data/service/ClientService.java | 45 +++++++++++++++++++ .../ccms/data/service/TransactionService.java | 25 ----------- 6 files changed, 117 insertions(+), 31 deletions(-) create mode 100644 data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java delete mode 100644 data-service/src/main/java/uk/gov/laa/ccms/data/service/TransactionService.java diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/controller/ClientsController.java b/data-service/src/main/java/uk/gov/laa/ccms/data/controller/ClientsController.java index b2e375e6..28efce7a 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/controller/ClientsController.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/controller/ClientsController.java @@ -4,17 +4,36 @@ import org.springframework.web.bind.annotation.RestController; import uk.gov.laa.ccms.data.api.ClientsApi; import uk.gov.laa.ccms.data.model.TransactionStatus; -import uk.gov.laa.ccms.data.service.TransactionService; +import uk.gov.laa.ccms.data.service.ClientService; +/** + * Controller class responsible for handling client-related requests. + * + *

This controller serves as an interface to return requested client + * information. It delegates the business logic to the {@link ClientService}.

+ * + *

This class implements the {@link ClientsApi} interface and provides endpoints + * for retrieving client based information for users.

+ * + * @see ClientsApi + * @see ClientService + * @author Jamie Briggs + */ @RestController public class ClientsController implements ClientsApi { - private final TransactionService transactionService; + private final ClientService transactionService; - public ClientsController(TransactionService transactionService) { + public ClientsController(ClientService transactionService) { this.transactionService = transactionService; } + /** + * Retrieves the transaction status for a client based on the transaction request ID. + * + * @param transactionRequestId the transaction request ID (required) + * @return a {@code ResponseEntity} containing the related transaction status. + */ @Override public ResponseEntity getClientTransactionStatus(String transactionRequestId) { return transactionService.getTransactionStatus(transactionRequestId).map(ResponseEntity::ok) diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/controller/NotificationsController.java b/data-service/src/main/java/uk/gov/laa/ccms/data/controller/NotificationsController.java index 9b27175b..9703f28c 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/controller/NotificationsController.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/controller/NotificationsController.java @@ -15,10 +15,14 @@ * Controller class responsible for handling notification-related requests. * *

This controller serves as an interface to return requested user notification - * information. It delegates the business logic to the {@link NotificationService}. + * information. It delegates the business logic to the {@link NotificationService}.

* *

This class implements the {@link NotificationsApi} interface and provides - * endpoints for retrieving notification summaries for users. + * endpoints for retrieving notification summaries for users.

+ * + * @see NotificationsApi + * @see NotificationService + * @author Jamie Briggs */ @RestController @RequiredArgsConstructor diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/mapper/TransactionStatusMapper.java b/data-service/src/main/java/uk/gov/laa/ccms/data/mapper/TransactionStatusMapper.java index db54e804..6ad8365d 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/mapper/TransactionStatusMapper.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/mapper/TransactionStatusMapper.java @@ -5,13 +5,35 @@ import org.mapstruct.Named; import uk.gov.laa.ccms.data.model.TransactionStatus; +/** + * Interface responsible for mapping {@link uk.gov.laa.ccms.data.entity.TransactionStatus} entities + * to {@link TransactionStatus} objects. This interface utilizes MapStruct for transformation. + * + * @see uk.gov.laa.ccms.data.entity.TransactionStatus + * @see TransactionStatus + * @author Jamie Briggs + */ @Mapper(componentModel = "spring") public interface TransactionStatusMapper { + /** + * Maps a {@link uk.gov.laa.ccms.data.entity.TransactionStatus} to a {@link TransactionStatus} + * object. + * + * @param entity the source {@link uk.gov.laa.ccms.data.entity.TransactionStatus} object. + * @return a {@link TransactionStatus} object mapped from the source + * {@link uk.gov.laa.ccms.data.entity.TransactionStatus} object. + */ @Mapping(target = "submissionStatus", source = "status", qualifiedByName = "toUpperCase") @Mapping(target = "referenceNumber", source = "recordRefValue") TransactionStatus toTransactionStatus(uk.gov.laa.ccms.data.entity.TransactionStatus entity); + /** + * Converts the given string to uppercase. If the input string is null, the method returns null. + * + * @param value the input string to be converted to uppercase; may be null. + * @return the uppercase version of the input string, or null if the input was null. + */ @Named("toUpperCase") static String toUpperCase(String value) { return value == null ? null : value.toUpperCase(); diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java index fd217f8c..3fcfbe99 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java @@ -5,9 +5,30 @@ import org.springframework.stereotype.Repository; import uk.gov.laa.ccms.data.entity.TransactionStatus; +/** + * Repository interface for accessing {@link TransactionStatus} entities. + * + *

This repository extends the {@link ReadOnlyRepository} interface, which supports + * read-only operations for the {@link TransactionStatus} entity.

+ * + * @see TransactionStatus + * @see ReadOnlyRepository + * @author Jamie Briggs + */ @Repository public interface TransactionStatusRepository extends ReadOnlyRepository { - @Query("SELECT ts FROM TransactionStatus ts WHERE ts.requestId = ?1 AND ts.processName = 'CreateClient'") + /** + * Finds a client transaction with a specific transaction ID. + * + * @param transactionId the unique identifier of the transaction to search for + * @return an {@code Optional} containing the {@code TransactionStatus} if found, or an + * empty {@code Optional} if not found. + */ + @Query(""" + SELECT ts FROM TransactionStatus ts + WHERE ts.requestId = ?1 + AND ts.processName = 'CreateClient' + """) Optional findClientTransactionByTransactionId(String transactionId); } diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java b/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java new file mode 100644 index 00000000..ab87b5e0 --- /dev/null +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java @@ -0,0 +1,45 @@ +package uk.gov.laa.ccms.data.service; + +import java.util.Optional; +import org.springframework.stereotype.Service; +import uk.gov.laa.ccms.data.mapper.TransactionStatusMapper; +import uk.gov.laa.ccms.data.model.TransactionStatus; +import uk.gov.laa.ccms.data.repository.TransactionStatusRepository; + +/** + * Service class responsible for handling client-related operations. + * + *

This class provides methods to retrieve transaction statuses for client-related transactions. + * It utilizes a repository for database access and a mapper for converting database entities + * to objects used in the application.

+ * + * @see TransactionStatusRepository + * @see TransactionStatusMapper + * @author Jamie Briggs + */ +@Service +public class ClientService { + + private final TransactionStatusMapper transactionStatusMapper; + private final TransactionStatusRepository transactionStatusRepository; + + public ClientService(TransactionStatusMapper transactionStatusMapper, + TransactionStatusRepository transactionStatusRepository) { + this.transactionStatusMapper = transactionStatusMapper; + this.transactionStatusRepository = transactionStatusRepository; + } + + /** + * Retrieves the transaction status for a given transaction ID. If the transaction + * is not found, an empty {@code Optional} is returned. + * + * @param transactionId the unique identifier of the transaction whose status + * is to be retrieved + * @return an {@code Optional} containing the {@link TransactionStatus} if found, + * or an empty {@code Optional} if not found + */ + public Optional getTransactionStatus(String transactionId) { + return transactionStatusRepository.findClientTransactionByTransactionId(transactionId).map( + transactionStatusMapper::toTransactionStatus); + } +} diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/service/TransactionService.java b/data-service/src/main/java/uk/gov/laa/ccms/data/service/TransactionService.java deleted file mode 100644 index 074dfc6c..00000000 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/service/TransactionService.java +++ /dev/null @@ -1,25 +0,0 @@ -package uk.gov.laa.ccms.data.service; - -import java.util.Optional; -import org.springframework.stereotype.Service; -import uk.gov.laa.ccms.data.mapper.TransactionStatusMapper; -import uk.gov.laa.ccms.data.model.TransactionStatus; -import uk.gov.laa.ccms.data.repository.TransactionStatusRepository; - -@Service -public class TransactionService { - - private final TransactionStatusMapper transactionStatusMapper; - private final TransactionStatusRepository transactionStatusRepository; - - public TransactionService(TransactionStatusMapper transactionStatusMapper, - TransactionStatusRepository transactionStatusRepository) { - this.transactionStatusMapper = transactionStatusMapper; - this.transactionStatusRepository = transactionStatusRepository; - } - - public Optional getTransactionStatus(String transactionId) { - return transactionStatusRepository.findClientTransactionByTransactionId(transactionId).map( - transactionStatusMapper::toTransactionStatus); - } -} From 978a965dc8dcea2e09f12bd1b0211660d09bd8dd Mon Sep 17 00:00:00 2001 From: Jamie Briggs Date: Tue, 21 Jan 2025 16:13:52 +0000 Subject: [PATCH 03/12] CCMSPUI-381 Completed TransactionStatusRepositoryIntegrationTest Signed-off-by: Jamie Briggs --- ...actionStatusRepositoryIntegrationTest.java | 175 ++++++++++++++++++ .../ccms/data/entity/TransactionStatus.java | 21 ++- .../TransactionStatusRepository.java | 11 +- .../controller/ClientsControllerTest.java | 72 +++++++ 4 files changed, 275 insertions(+), 4 deletions(-) create mode 100644 data-service/src/integrationTest/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepositoryIntegrationTest.java create mode 100644 data-service/src/test/java/uk/gov/laa/ccms/data/controller/ClientsControllerTest.java diff --git a/data-service/src/integrationTest/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepositoryIntegrationTest.java b/data-service/src/integrationTest/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepositoryIntegrationTest.java new file mode 100644 index 00000000..44de91d2 --- /dev/null +++ b/data-service/src/integrationTest/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepositoryIntegrationTest.java @@ -0,0 +1,175 @@ +package uk.gov.laa.ccms.data.repository; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.ActiveProfiles; +import uk.gov.laa.ccms.data.entity.TransactionStatus; + +@DataJpaTest +@ActiveProfiles("h2-test") +@DisplayName("Transaction Status Repository Integration Test") +public class TransactionStatusRepositoryIntegrationTest { + + @Autowired + private TransactionStatusRepository transactionStatusRepository; + + @PersistenceContext + private EntityManager entityManager; + + private TransactionStatus createClientTransactionStatus; + private TransactionStatus updateClientTransactionStatus; + private TransactionStatus userFuncOne; + private TransactionStatus userFuncTwo; + private TransactionStatus userFuncErr; + + @BeforeEach + void setUp() { + // Insert test data into the in-memory database + createClientTransactionStatus = TransactionStatus.builder() + .requestId("1") + .processName("CreateClient") + .recordRefKey("CLIENT_REF_NUMBER") + .recordRefValue("6505") + .status("Success") + .errorDescription("Party Successfully Created") + .transactionOccurrenceDate(LocalDateTime.of(2024, 1, 1, 1, 1)) + .build(); + updateClientTransactionStatus = TransactionStatus.builder() + .requestId("2") + .processName("UpdateClient") + .recordRefKey("CLIENT_REF_NUMBER") + .recordRefValue("6505") + .status("Success") + .errorDescription("Party Successfully Created") + .transactionOccurrenceDate(LocalDateTime.of(2024, 1, 1, 1, 2)) + .build(); + // Both with same request ID, this happens in EBS + userFuncOne = TransactionStatus.builder() + .requestId("1") + .processName("XXCCMS_COMMON_UTIL.USER_FUNC_AUTH") + .recordRefKey("CLIENT_REF_NUMBER") + .recordRefValue("6505") + .status("Success") + .errorDescription("Party Successfully Created") + .transactionOccurrenceDate(LocalDateTime.of(2024, 1, 1, 1, 3)) + .build(); + userFuncTwo = TransactionStatus.builder() + .requestId("1") + .processName("XXCCMS_COMMON_UTIL.USER_FUNC_AUTH") + .recordRefKey("CLIENT_REF_NUMBER") + .recordRefValue("6505") + .status("Success") + .errorDescription("Party Successfully Created") + .transactionOccurrenceDate(LocalDateTime.of(2024, 1, 1, 1, 4)) + .build(); + userFuncErr = TransactionStatus.builder() + .requestId("500") + .processName("USER_FUNC_AUTH") + .recordRefKey("CLIENT_REF_NUMBER") + .recordRefValue("6505") + .status("Error") + .errorDescription("Party Successfully Created") + .transactionOccurrenceDate(LocalDateTime.of(2024, 1, 1, 1, 5)) + .build(); + + // Use entityManager as NotificationRepository extends ReadOnlyRepository. + entityManager.persist(createClientTransactionStatus); + entityManager.persist(updateClientTransactionStatus); + entityManager.persist(userFuncOne); + entityManager.persist(userFuncTwo); + entityManager.persist(userFuncErr); + } + + @Nested + @DisplayName("findClientTransactionByTransactionId() Tests") + class FindClientTransactionByTransactionIdTests { + + @Test + @DisplayName("Should not return client transaction status") + void shouldNotReturnClientTransactionStatus() { + // Given + String requestId = "404"; + // When + Optional result = + transactionStatusRepository.findClientTransactionByTransactionId( + requestId); + // Then + assertTrue(result.isEmpty()); + } + + @Test + @DisplayName("Should get only create client transaction status") + void shouldGetOnlyCreateClientTransactionStatus() { + // Given + String requestId = "1"; + // When + Optional result = + transactionStatusRepository.findClientTransactionByTransactionId( + requestId); + // Then + assertFalse(result.isEmpty()); + assertEquals(createClientTransactionStatus, result.get()); + } + + @Test + @DisplayName("Should get only update client transaction status") + void shouldGetOnlyUpdateClientTransactionStatus() { + // Given + String requestId = "2"; + // When + Optional result = + transactionStatusRepository.findClientTransactionByTransactionId( + requestId); + // Then + assertFalse(result.isEmpty()); + assertEquals(updateClientTransactionStatus, result.get()); + } + } + + @Nested + @DisplayName("findUserFunctionTransactionsByTransactionId() Tests") + class FindUserFunctionTransactionsByTransactionIdTests { + + @Test + @DisplayName("Should get multiple user function transactions") + void shouldGetMultipleUserFunctionTransactions() { + // Given + String requestId = "1"; + // When + List result = + transactionStatusRepository.findUserFunctionTransactionsByTransactionId( + requestId); + // Then + assertEquals(2, result.size()); + assertTrue(result.contains(userFuncOne)); + assertTrue(result.contains(userFuncTwo)); + } + + @Test + @DisplayName("Should get error user function transactions") + void shouldGetErrorUserFunctionTransactions() { + // Given + String requestId = "500"; + // When + List result = + transactionStatusRepository.findUserFunctionTransactionsByTransactionId( + requestId); + // Then + assertEquals(1, result.size()); + assertTrue(result.contains(userFuncErr)); + } + } +} diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java index ab797cc2..25761381 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java @@ -4,12 +4,17 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; +import jakarta.persistence.IdClass; import jakarta.persistence.Table; -import java.sql.Date; +import java.io.Serializable; +import java.time.LocalDateTime; import lombok.AllArgsConstructor; import lombok.Builder; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.Setter; +import uk.gov.laa.ccms.data.entity.TransactionStatus.TransactionStatusId; /** * Represents a transaction status entity from the "XXCCMS_TRANSACTION_STATUS_V" database view. @@ -24,6 +29,7 @@ @Table(name = "XXCCMS_TRANSACTION_STATUS_V") @Getter @Builder +@IdClass(TransactionStatusId.class) @AllArgsConstructor @RequiredArgsConstructor public class TransactionStatus { @@ -32,6 +38,10 @@ public class TransactionStatus { @Column(name = "REQUEST_ID") private String requestId; + @Id + @Column(name = "TRANSACTION_OCCURRENCE_DATE") + private LocalDateTime transactionOccurrenceDate; + @Column(name = "PROCESS_NAME", length = 50) private String processName; @@ -47,6 +57,11 @@ public class TransactionStatus { @Column(name = "ERROR_DESCRIPTION", length = 2000) private String errorDescription; - @Column(name = "TRANSACTION_OCCURRENCE_DATE") - private Date transactionOccurrenceDate; + @Getter + @Setter + @EqualsAndHashCode + public static class TransactionStatusId implements Serializable { + private String requestId; + private LocalDateTime transactionOccurrenceDate; + } } diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java index 3fcfbe99..de1ec562 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java @@ -1,5 +1,6 @@ package uk.gov.laa.ccms.data.repository; +import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; @@ -18,6 +19,13 @@ @Repository public interface TransactionStatusRepository extends ReadOnlyRepository { + @Query(""" + SELECT ts FROM TransactionStatus ts + WHERE ts.requestId = ?1 + AND ts.processName like '%USER_FUNC_AUTH%' + """) + List findUserFunctionTransactionsByTransactionId(String transactionId); + /** * Finds a client transaction with a specific transaction ID. * @@ -28,7 +36,8 @@ public interface TransactionStatusRepository extends ReadOnlyRepository findClientTransactionByTransactionId(String transactionId); + } diff --git a/data-service/src/test/java/uk/gov/laa/ccms/data/controller/ClientsControllerTest.java b/data-service/src/test/java/uk/gov/laa/ccms/data/controller/ClientsControllerTest.java new file mode 100644 index 00000000..9ca1be78 --- /dev/null +++ b/data-service/src/test/java/uk/gov/laa/ccms/data/controller/ClientsControllerTest.java @@ -0,0 +1,72 @@ +package uk.gov.laa.ccms.data.controller; + +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; + +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.context.WebApplicationContext; +import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; +import uk.gov.laa.ccms.data.model.TransactionStatus; +import uk.gov.laa.ccms.data.service.ClientService; + +@ExtendWith(MockitoExtension.class) +@ContextConfiguration +@WebAppConfiguration +class ClientsControllerTest { + + @Mock + private ClientService clientService; + + @InjectMocks + private ClientsController clientsController; + + private MockMvc mockMvc; + private ObjectMapper objectMapper; + + @Autowired + WebApplicationContext webApplicationContext; + + @BeforeEach + public void setup(){ + mockMvc = standaloneSetup(clientsController).build(); + objectMapper = new ObjectMapper(); + } + + @Test + @DisplayName("getClientTransactionStatus() - Returns Data") + void getClientTransactionStatus_returnsData() throws Exception { + // Given + TransactionStatus status = new TransactionStatus().submissionStatus("status").referenceNumber("123"); + when(clientService.getTransactionStatus("ABCDEF")).thenReturn(Optional.of(status)); + String jsonContent = objectMapper.writeValueAsString(status); + // Then + this.mockMvc.perform(get("/clients/status/ABCDEF")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().json(jsonContent)); + } + + @Test + @DisplayName("getClientTransactionStatus() - Returns not found") + void getClientTransactionStatus_returnsNotFound() throws Exception { + // Then + this.mockMvc.perform(get("/clients/status/ABCDEF")) + .andDo(print()) + .andExpect(status().isNotFound()); + } +} From b3af35ddcdb68f71a1bde107248f8e9e22181cce Mon Sep 17 00:00:00 2001 From: Jamie Briggs Date: Tue, 21 Jan 2025 16:39:31 +0000 Subject: [PATCH 04/12] CCMSPUI-381 Added check for error transactions before attempting to find actual client transactions. Updated tests to correlate with this. Signed-off-by: Jamie Briggs --- ...actionStatusRepositoryIntegrationTest.java | 4 +- .../data/controller/ClientsController.java | 7 +- .../ccms/data/entity/TransactionStatus.java | 6 + .../TransactionStatusRepository.java | 2 +- .../laa/ccms/data/service/ClientService.java | 18 ++- .../data/service/ClientServiceException.java | 15 ++ .../controller/ClientsControllerTest.java | 12 ++ .../ccms/data/service/ClientServiceTest.java | 133 ++++++++++++++++++ 8 files changed, 190 insertions(+), 7 deletions(-) create mode 100644 data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientServiceException.java create mode 100644 data-service/src/test/java/uk/gov/laa/ccms/data/service/ClientServiceTest.java diff --git a/data-service/src/integrationTest/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepositoryIntegrationTest.java b/data-service/src/integrationTest/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepositoryIntegrationTest.java index 44de91d2..3c595541 100644 --- a/data-service/src/integrationTest/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepositoryIntegrationTest.java +++ b/data-service/src/integrationTest/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepositoryIntegrationTest.java @@ -150,7 +150,7 @@ void shouldGetMultipleUserFunctionTransactions() { String requestId = "1"; // When List result = - transactionStatusRepository.findUserFunctionTransactionsByTransactionId( + transactionStatusRepository.findAllUserFunctionTransactionsByTransactionId( requestId); // Then assertEquals(2, result.size()); @@ -165,7 +165,7 @@ void shouldGetErrorUserFunctionTransactions() { String requestId = "500"; // When List result = - transactionStatusRepository.findUserFunctionTransactionsByTransactionId( + transactionStatusRepository.findAllUserFunctionTransactionsByTransactionId( requestId); // Then assertEquals(1, result.size()); diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/controller/ClientsController.java b/data-service/src/main/java/uk/gov/laa/ccms/data/controller/ClientsController.java index 28efce7a..70dbb234 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/controller/ClientsController.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/controller/ClientsController.java @@ -5,6 +5,7 @@ import uk.gov.laa.ccms.data.api.ClientsApi; import uk.gov.laa.ccms.data.model.TransactionStatus; import uk.gov.laa.ccms.data.service.ClientService; +import uk.gov.laa.ccms.data.service.ClientServiceException; /** * Controller class responsible for handling client-related requests. @@ -36,7 +37,11 @@ public ClientsController(ClientService transactionService) { */ @Override public ResponseEntity getClientTransactionStatus(String transactionRequestId) { - return transactionService.getTransactionStatus(transactionRequestId).map(ResponseEntity::ok) + try { + return transactionService.getTransactionStatus(transactionRequestId).map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); + } catch (ClientServiceException e) { + return ResponseEntity.internalServerError().build(); + } } } diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java index 25761381..54688c8a 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java @@ -57,6 +57,12 @@ public class TransactionStatus { @Column(name = "ERROR_DESCRIPTION", length = 2000) private String errorDescription; + /** + * This class represents the composite primary key for the TransactionStatus entity. + * + * @author Jamie Briggs + * @see TransactionStatus + */ @Getter @Setter @EqualsAndHashCode diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java index de1ec562..b14895d5 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java @@ -24,7 +24,7 @@ public interface TransactionStatusRepository extends ReadOnlyRepository findUserFunctionTransactionsByTransactionId(String transactionId); + List findAllUserFunctionTransactionsByTransactionId(String transactionId); /** * Finds a client transaction with a specific transaction ID. diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java b/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java index ab87b5e0..fc4b012e 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java @@ -1,5 +1,6 @@ package uk.gov.laa.ccms.data.service; +import java.util.List; import java.util.Optional; import org.springframework.stereotype.Service; import uk.gov.laa.ccms.data.mapper.TransactionStatusMapper; @@ -37,9 +38,20 @@ public ClientService(TransactionStatusMapper transactionStatusMapper, * is to be retrieved * @return an {@code Optional} containing the {@link TransactionStatus} if found, * or an empty {@code Optional} if not found + * @throws ClientServiceException throws exception when there was an error found + * with the associated transaction ID */ - public Optional getTransactionStatus(String transactionId) { - return transactionStatusRepository.findClientTransactionByTransactionId(transactionId).map( - transactionStatusMapper::toTransactionStatus); + public Optional getTransactionStatus(String transactionId) + throws ClientServiceException { + List userFuncStatus = + transactionStatusRepository.findAllUserFunctionTransactionsByTransactionId(transactionId); + if (userFuncStatus.stream().anyMatch(x -> x.getStatus() + .equalsIgnoreCase("ERROR"))) { + throw new ClientServiceException("Error found in user function"); + } + return transactionStatusRepository.findClientTransactionByTransactionId(transactionId) + .map(transactionStatusMapper::toTransactionStatus); } + + } diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientServiceException.java b/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientServiceException.java new file mode 100644 index 00000000..61dacbef --- /dev/null +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientServiceException.java @@ -0,0 +1,15 @@ +package uk.gov.laa.ccms.data.service; + +/** + * Custom exception class used to handle errors specific to the ClientService. + * + *

This exception is thrown when a client-related operation encounters an error + * such as a user function transaction error.

+ * + * @author Jamie Briggs + */ +public class ClientServiceException extends RuntimeException { + public ClientServiceException(String message) { + super(message); + } +} diff --git a/data-service/src/test/java/uk/gov/laa/ccms/data/controller/ClientsControllerTest.java b/data-service/src/test/java/uk/gov/laa/ccms/data/controller/ClientsControllerTest.java index 9ca1be78..c9084635 100644 --- a/data-service/src/test/java/uk/gov/laa/ccms/data/controller/ClientsControllerTest.java +++ b/data-service/src/test/java/uk/gov/laa/ccms/data/controller/ClientsControllerTest.java @@ -23,6 +23,7 @@ import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; import uk.gov.laa.ccms.data.model.TransactionStatus; import uk.gov.laa.ccms.data.service.ClientService; +import uk.gov.laa.ccms.data.service.ClientServiceException; @ExtendWith(MockitoExtension.class) @ContextConfiguration @@ -69,4 +70,15 @@ void getClientTransactionStatus_returnsNotFound() throws Exception { .andDo(print()) .andExpect(status().isNotFound()); } + + @Test + @DisplayName("getClientTransactionStatus() - Returns 500 error") + void getClientTransactionStatus_returns500Error() throws Exception { + // When + when(clientService.getTransactionStatus("ABCDEF")).thenThrow(new ClientServiceException("error")); + // Then + this.mockMvc.perform(get("/clients/status/ABCDEF")) + .andDo(print()) + .andExpect(status().is5xxServerError()); + } } diff --git a/data-service/src/test/java/uk/gov/laa/ccms/data/service/ClientServiceTest.java b/data-service/src/test/java/uk/gov/laa/ccms/data/service/ClientServiceTest.java new file mode 100644 index 00000000..de7138d0 --- /dev/null +++ b/data-service/src/test/java/uk/gov/laa/ccms/data/service/ClientServiceTest.java @@ -0,0 +1,133 @@ +package uk.gov.laa.ccms.data.service; + +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.laa.ccms.data.mapper.TransactionStatusMapper; +import uk.gov.laa.ccms.data.model.TransactionStatus; +import uk.gov.laa.ccms.data.repository.TransactionStatusRepository; + +@ExtendWith(MockitoExtension.class) +@DisplayName("ClientServiceTest") +class ClientServiceTest { + + @Mock + TransactionStatusMapper transactionStatusMapper; + @Mock + TransactionStatusRepository transactionStatusRepository; + + ClientService clientService; + + @BeforeEach + void beforeEach() { + clientService = + new ClientService(transactionStatusMapper, transactionStatusRepository); + } + + @Test + @DisplayName("Should return empty client transaction status") + void shouldReturnEmptyClientTransactionStatus() { + // Given + String transactionId = "123"; + // When + Optional transactionStatus = clientService.getTransactionStatus( + transactionId); + // Then + assertTrue(transactionStatus.isEmpty()); + } + + @Test + @DisplayName("Should return client transaction status") + void shouldReturnClientTransactionStatus() { + // Given + String transactionId = "123"; + uk.gov.laa.ccms.data.entity.TransactionStatus entity = + uk.gov.laa.ccms.data.entity.TransactionStatus.builder() + .requestId(transactionId) + .processName("CreateClient").status("Success").build(); + when(transactionStatusRepository.findClientTransactionByTransactionId(transactionId)) + .thenReturn(Optional.of(entity)); + TransactionStatus result = new TransactionStatus().submissionStatus("Success") + .referenceNumber("123"); + when(transactionStatusMapper.toTransactionStatus(entity)).thenReturn( + result); + // When + Optional transactionStatus = clientService.getTransactionStatus( + transactionId); + // Then + verify(transactionStatusRepository, times(1)).findAllUserFunctionTransactionsByTransactionId( + transactionId); + verify(transactionStatusRepository, times(1)).findClientTransactionByTransactionId( + transactionId); + assertTrue(transactionStatus.isPresent()); + assertEquals(result, transactionStatus.get()); + } + + @Test + @DisplayName("Should return client transaction status even with success transaction") + void shouldReturnClientTransactionStatusEvenWithSuccessTransactions() { + // Given + String transactionId = "123"; + uk.gov.laa.ccms.data.entity.TransactionStatus successTransaction = + uk.gov.laa.ccms.data.entity.TransactionStatus.builder() + .requestId(transactionId) + .processName("XXCCMS_COMMON_UTIL.USER_FUNC_AUTH").status("Success").build(); + when(transactionStatusRepository.findAllUserFunctionTransactionsByTransactionId(transactionId)) + .thenReturn(singletonList(successTransaction)); + uk.gov.laa.ccms.data.entity.TransactionStatus entity = + uk.gov.laa.ccms.data.entity.TransactionStatus.builder() + .requestId(transactionId) + .processName("CreateClient").status("Success").build(); + when(transactionStatusRepository.findClientTransactionByTransactionId(transactionId)) + .thenReturn(Optional.of(entity)); + TransactionStatus result = new TransactionStatus().submissionStatus("Success") + .referenceNumber("123"); + when(transactionStatusMapper.toTransactionStatus(entity)).thenReturn( + result); + + // When + Optional transactionStatus = clientService.getTransactionStatus( + transactionId); + + // Then + verify(transactionStatusRepository, times(1)).findAllUserFunctionTransactionsByTransactionId( + transactionId); + verify(transactionStatusRepository, times(1)).findClientTransactionByTransactionId( + transactionId); + assertTrue(transactionStatus.isPresent()); + assertEquals(result, transactionStatus.get()); + } + + @Test + @DisplayName("Should throw exception when user function transaction is error") + void shouldThrowExceptionWhenUserFunctionTransactionIsError() { + // Given + String transactionId = "123"; + uk.gov.laa.ccms.data.entity.TransactionStatus successTransaction = + uk.gov.laa.ccms.data.entity.TransactionStatus.builder() + .requestId(transactionId) + .processName("XXCCMS_COMMON_UTIL.USER_FUNC_AUTH").status("Error").build(); + when(transactionStatusRepository.findAllUserFunctionTransactionsByTransactionId(transactionId)) + .thenReturn(singletonList(successTransaction)); + + // When & Then + assertThrows(ClientServiceException.class, () -> clientService.getTransactionStatus(transactionId)); + verify(transactionStatusRepository, times(1)).findAllUserFunctionTransactionsByTransactionId( + transactionId); + verify(transactionStatusRepository, times(0)).findClientTransactionByTransactionId( + transactionId); + } + +} \ No newline at end of file From 575639f0d2c0c9176f8114877a68ae9aa57f2e6b Mon Sep 17 00:00:00 2001 From: Jamie Briggs Date: Wed, 22 Jan 2025 10:23:47 +0000 Subject: [PATCH 05/12] CCMSPUI-381 Added schema to TransactionStatus Signed-off-by: Jamie Briggs --- .../java/uk/gov/laa/ccms/data/entity/TransactionStatus.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java index 54688c8a..82b7d586 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java @@ -26,7 +26,7 @@ *

This class is immutable, and its instances can be created using the builder pattern.

*/ @Entity -@Table(name = "XXCCMS_TRANSACTION_STATUS_V") +@Table(name = "XXCCMS_TRANSACTION_STATUS_V", schema = "XXCCMS") @Getter @Builder @IdClass(TransactionStatusId.class) From c47fab3120f085892cdc3026f8c75326be2631a4 Mon Sep 17 00:00:00 2001 From: Jamie Briggs Date: Wed, 22 Jan 2025 15:47:57 +0000 Subject: [PATCH 06/12] CCMSPUI-382 TransactionStatusRepository updated to be able to search for case transactions Signed-off-by: Jamie Briggs --- data-api/open-api-specification.yml | 30 ++++++++ ...actionStatusRepositoryIntegrationTest.java | 69 +++++++++++++++++++ .../data/controller/CaseSearchController.java | 6 ++ .../TransactionStatusRepository.java | 14 ++++ .../laa/ccms/data/service/CaseService.java | 8 +++ 5 files changed, 127 insertions(+) create mode 100644 data-service/src/main/java/uk/gov/laa/ccms/data/service/CaseService.java diff --git a/data-api/open-api-specification.yml b/data-api/open-api-specification.yml index 1c07cd36..c506fe76 100644 --- a/data-api/open-api-specification.yml +++ b/data-api/open-api-specification.yml @@ -470,6 +470,36 @@ paths: description: 'Not found' '500': description: 'Internal server error' + /cases/status/{transaction-request-id}: + get: + tags: + - cases + summary: 'Get Case transaction status' + operationId: 'getCaseTransactionStatus' + parameters: + - name: 'transaction-request-id' + in: 'path' + required: true + schema: + type: 'string' + example: 'abc123' + responses: + '200': + description: 'Successful operation' + content: + application/json: + schema: + $ref: "#/components/schemas/transactionStatus" + '400': + description: 'Bad request' + '401': + description: 'Unauthorized' + '403': + description: 'Forbidden' + '404': + description: 'Not found' + '500': + description: 'Internal server error' /lookup/case-status: get: tags: diff --git a/data-service/src/integrationTest/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepositoryIntegrationTest.java b/data-service/src/integrationTest/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepositoryIntegrationTest.java index 3c595541..b5fd18f1 100644 --- a/data-service/src/integrationTest/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepositoryIntegrationTest.java +++ b/data-service/src/integrationTest/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepositoryIntegrationTest.java @@ -31,6 +31,8 @@ public class TransactionStatusRepositoryIntegrationTest { private TransactionStatus createClientTransactionStatus; private TransactionStatus updateClientTransactionStatus; + private TransactionStatus createCaseApplicationTransactionStatus; + private TransactionStatus updateCaseApplicationTransactionStatus; private TransactionStatus userFuncOne; private TransactionStatus userFuncTwo; private TransactionStatus userFuncErr; @@ -56,6 +58,25 @@ void setUp() { .errorDescription("Party Successfully Created") .transactionOccurrenceDate(LocalDateTime.of(2024, 1, 1, 1, 2)) .build(); + createCaseApplicationTransactionStatus = TransactionStatus.builder() + .requestId("3") + .processName("CreateCaseApplication") + .recordRefKey("CLIENT_REF_NUMBER") + .recordRefValue("6505") + .status("Success") + .errorDescription("Party Successfully Created") + .transactionOccurrenceDate(LocalDateTime.of(2024, 1, 1, 1, 3)) + .build(); + updateCaseApplicationTransactionStatus = TransactionStatus.builder() + .requestId("4") + .processName("UpdateCaseApplication") + .recordRefKey("CLIENT_REF_NUMBER") + .recordRefValue("6505") + .status("Success") + .errorDescription("Party Successfully Created") + .transactionOccurrenceDate(LocalDateTime.of(2024, 1, 1, 1, 4)) + .build(); + // Both with same request ID, this happens in EBS userFuncOne = TransactionStatus.builder() .requestId("1") @@ -88,6 +109,8 @@ void setUp() { // Use entityManager as NotificationRepository extends ReadOnlyRepository. entityManager.persist(createClientTransactionStatus); entityManager.persist(updateClientTransactionStatus); + entityManager.persist(createCaseApplicationTransactionStatus); + entityManager.persist(updateCaseApplicationTransactionStatus); entityManager.persist(userFuncOne); entityManager.persist(userFuncTwo); entityManager.persist(userFuncErr); @@ -139,6 +162,52 @@ void shouldGetOnlyUpdateClientTransactionStatus() { } } + @Nested + @DisplayName("findCaseApplicationTransactionByTransactionId() Tests") + class FindCaseApplicationTransactionByTransactionIdTests { + + @Test + @DisplayName("Should not return case transaction status") + void shouldNotReturnCaseTransactionStatus() { + // Given + String requestId = "404"; + // When + Optional result = + transactionStatusRepository.findCaseApplicationTransactionByTransactionId( + requestId); + // Then + assertTrue(result.isEmpty()); + } + + @Test + @DisplayName("Should get only create case transaction status") + void shouldGetOnlyCreateCaseTransactionStatus() { + // Given + String requestId = "3"; + // When + Optional result = + transactionStatusRepository.findCaseApplicationTransactionByTransactionId( + requestId); + // Then + assertFalse(result.isEmpty()); + assertEquals(createCaseApplicationTransactionStatus, result.get()); + } + + @Test + @DisplayName("Should get only update case transaction status") + void shouldGetOnlyUpdateCaseTransactionStatus() { + // Given + String requestId = "4"; + // When + Optional result = + transactionStatusRepository.findCaseApplicationTransactionByTransactionId( + requestId); + // Then + assertFalse(result.isEmpty()); + assertEquals(updateCaseApplicationTransactionStatus, result.get()); + } + } + @Nested @DisplayName("findUserFunctionTransactionsByTransactionId() Tests") class FindUserFunctionTransactionsByTransactionIdTests { diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/controller/CaseSearchController.java b/data-service/src/main/java/uk/gov/laa/ccms/data/controller/CaseSearchController.java index 03c879d5..e177312a 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/controller/CaseSearchController.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/controller/CaseSearchController.java @@ -7,6 +7,7 @@ import org.springframework.web.bind.annotation.RestController; import uk.gov.laa.ccms.data.api.CasesApi; import uk.gov.laa.ccms.data.model.CaseDetails; +import uk.gov.laa.ccms.data.model.TransactionStatus; import uk.gov.laa.ccms.data.service.CaseSearchService; /** @@ -53,4 +54,9 @@ public ResponseEntity getCases(Long providerFirmPartyId, String cas return cases.map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build()); } + + @Override + public ResponseEntity getCaseTransactionStatus(String transactionRequestId) { + return null; + } } diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java index b14895d5..d903327d 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java @@ -40,4 +40,18 @@ public interface TransactionStatusRepository extends ReadOnlyRepository findClientTransactionByTransactionId(String transactionId); + /** + * Finds a case transaction with a specific transaction ID. + * + * @param transactionId the unique identifier of the transaction to search for + * @return an {@code Optional} containing the {@code TransactionStatus} if found, or an + * empty {@code Optional} if not found. + */ + @Query(""" + SELECT ts FROM TransactionStatus ts + WHERE ts.requestId = ?1 + AND (ts.processName = 'CreateCaseApplication' OR ts.processName = 'UpdateCaseApplication') + """) + Optional findCaseApplicationTransactionByTransactionId(String transactionId); + } diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/service/CaseService.java b/data-service/src/main/java/uk/gov/laa/ccms/data/service/CaseService.java new file mode 100644 index 00000000..41e96b3f --- /dev/null +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/service/CaseService.java @@ -0,0 +1,8 @@ +package uk.gov.laa.ccms.data.service; + +import org.springframework.stereotype.Service; + +@Service +public class CaseService { + +} From 770fda8d2a1ed7b28e2481ab297461cc9b68beb6 Mon Sep 17 00:00:00 2001 From: Jamie Briggs Date: Wed, 22 Jan 2025 15:58:55 +0000 Subject: [PATCH 07/12] CCMSPUI-382 CaseService created ClientServiceTest Signed-off-by: Jamie Briggs --- .../laa/ccms/data/service/CaseService.java | 21 +++ .../laa/ccms/data/service/ClientService.java | 10 +- .../ccms/data/service/CaseServiceTest.java | 132 ++++++++++++++++++ .../ccms/data/service/ClientServiceTest.java | 4 +- 4 files changed, 158 insertions(+), 9 deletions(-) create mode 100644 data-service/src/test/java/uk/gov/laa/ccms/data/service/CaseServiceTest.java diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/service/CaseService.java b/data-service/src/main/java/uk/gov/laa/ccms/data/service/CaseService.java index 41e96b3f..0f589789 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/service/CaseService.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/service/CaseService.java @@ -1,8 +1,29 @@ package uk.gov.laa.ccms.data.service; +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import uk.gov.laa.ccms.data.mapper.TransactionStatusMapper; +import uk.gov.laa.ccms.data.model.TransactionStatus; +import uk.gov.laa.ccms.data.repository.TransactionStatusRepository; @Service +@RequiredArgsConstructor public class CaseService { + private final TransactionStatusRepository transactionStatusRepository; + private final TransactionStatusMapper transactionStatusMapper; + + + public Optional getTransactionStatus(String transactionId) { + List userFuncStatus = + transactionStatusRepository.findAllUserFunctionTransactionsByTransactionId(transactionId); + if (userFuncStatus.stream().anyMatch(x -> x.getStatus() + .equalsIgnoreCase("ERROR"))) { + throw new ClientServiceException("Error found in user function"); + } + return transactionStatusRepository.findCaseApplicationTransactionByTransactionId(transactionId) + .map(transactionStatusMapper::toTransactionStatus); + } } diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java b/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java index fc4b012e..c0cc7ab5 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java @@ -2,6 +2,7 @@ import java.util.List; import java.util.Optional; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import uk.gov.laa.ccms.data.mapper.TransactionStatusMapper; import uk.gov.laa.ccms.data.model.TransactionStatus; @@ -19,16 +20,11 @@ * @author Jamie Briggs */ @Service +@RequiredArgsConstructor public class ClientService { - private final TransactionStatusMapper transactionStatusMapper; private final TransactionStatusRepository transactionStatusRepository; - - public ClientService(TransactionStatusMapper transactionStatusMapper, - TransactionStatusRepository transactionStatusRepository) { - this.transactionStatusMapper = transactionStatusMapper; - this.transactionStatusRepository = transactionStatusRepository; - } + private final TransactionStatusMapper transactionStatusMapper; /** * Retrieves the transaction status for a given transaction ID. If the transaction diff --git a/data-service/src/test/java/uk/gov/laa/ccms/data/service/CaseServiceTest.java b/data-service/src/test/java/uk/gov/laa/ccms/data/service/CaseServiceTest.java new file mode 100644 index 00000000..cf605944 --- /dev/null +++ b/data-service/src/test/java/uk/gov/laa/ccms/data/service/CaseServiceTest.java @@ -0,0 +1,132 @@ +package uk.gov.laa.ccms.data.service; + +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.laa.ccms.data.mapper.TransactionStatusMapper; +import uk.gov.laa.ccms.data.model.TransactionStatus; +import uk.gov.laa.ccms.data.repository.TransactionStatusRepository; + +@ExtendWith(MockitoExtension.class) +@DisplayName("Case Service Test") +public class CaseServiceTest { + + + @Mock + TransactionStatusMapper transactionStatusMapper; + @Mock + TransactionStatusRepository transactionStatusRepository; + + CaseService caseService; + + @BeforeEach + void beforeEach(){ + caseService = + new CaseService(transactionStatusRepository, transactionStatusMapper); + } + + @Test + @DisplayName("Should return empty case transaction status") + void shouldReturnEmptyCaseTransactionStatus(){ + // Given + String transactionId = "123"; + // When + Optional transactionStatus = caseService.getTransactionStatus(transactionId); + // Then + assertTrue(transactionStatus.isEmpty()); + } + + @Test + @DisplayName("Should return case transaction status") + void shouldReturnCaseTransactionStatus() { + // Given + String transactionId = "123"; + uk.gov.laa.ccms.data.entity.TransactionStatus entity = + uk.gov.laa.ccms.data.entity.TransactionStatus.builder() + .requestId(transactionId) + .processName("CreateCaseApplication").status("Success").build(); + when(transactionStatusRepository.findCaseApplicationTransactionByTransactionId(transactionId)) + .thenReturn(Optional.of(entity)); + TransactionStatus result = new TransactionStatus().submissionStatus("Success") + .referenceNumber("123"); + when(transactionStatusMapper.toTransactionStatus(entity)).thenReturn( + result); + // When + Optional transactionStatus = caseService.getTransactionStatus( + transactionId); + // Then + verify(transactionStatusRepository, times(1)).findAllUserFunctionTransactionsByTransactionId( + transactionId); + verify(transactionStatusRepository, times(1)).findCaseApplicationTransactionByTransactionId( + transactionId); + assertTrue(transactionStatus.isPresent()); + assertEquals(result, transactionStatus.get()); + } + + @Test + @DisplayName("Should return case transaction status even with success transaction") + void shouldReturnCaseTransactionStatusEvenWithSuccessTransactions() { + // Given + String transactionId = "123"; + uk.gov.laa.ccms.data.entity.TransactionStatus successTransaction = + uk.gov.laa.ccms.data.entity.TransactionStatus.builder() + .requestId(transactionId) + .processName("XXCCMS_COMMON_UTIL.USER_FUNC_AUTH").status("Success").build(); + when(transactionStatusRepository.findAllUserFunctionTransactionsByTransactionId(transactionId)) + .thenReturn(singletonList(successTransaction)); + uk.gov.laa.ccms.data.entity.TransactionStatus entity = + uk.gov.laa.ccms.data.entity.TransactionStatus.builder() + .requestId(transactionId) + .processName("CreateCaseApplication").status("Success").build(); + when(transactionStatusRepository.findCaseApplicationTransactionByTransactionId(transactionId)) + .thenReturn(Optional.of(entity)); + TransactionStatus result = new TransactionStatus().submissionStatus("Success") + .referenceNumber("123"); + when(transactionStatusMapper.toTransactionStatus(entity)).thenReturn( + result); + + // When + Optional transactionStatus = caseService.getTransactionStatus( + transactionId); + + // Then + verify(transactionStatusRepository, times(1)).findAllUserFunctionTransactionsByTransactionId( + transactionId); + verify(transactionStatusRepository, times(1)).findCaseApplicationTransactionByTransactionId( + transactionId); + assertTrue(transactionStatus.isPresent()); + assertEquals(result, transactionStatus.get()); + } + + @Test + @DisplayName("Should throw exception when user function transaction is error") + void shouldThrowExceptionWhenUserFunctionTransactionIsError() { + // Given + String transactionId = "123"; + uk.gov.laa.ccms.data.entity.TransactionStatus successTransaction = + uk.gov.laa.ccms.data.entity.TransactionStatus.builder() + .requestId(transactionId) + .processName("XXCCMS_COMMON_UTIL.USER_FUNC_AUTH").status("Error").build(); + when(transactionStatusRepository.findAllUserFunctionTransactionsByTransactionId(transactionId)) + .thenReturn(singletonList(successTransaction)); + + // When & Then + assertThrows(ClientServiceException.class, () -> caseService.getTransactionStatus(transactionId)); + verify(transactionStatusRepository, times(1)).findAllUserFunctionTransactionsByTransactionId( + transactionId); + verify(transactionStatusRepository, times(0)).findCaseApplicationTransactionByTransactionId( + transactionId); + } +} diff --git a/data-service/src/test/java/uk/gov/laa/ccms/data/service/ClientServiceTest.java b/data-service/src/test/java/uk/gov/laa/ccms/data/service/ClientServiceTest.java index de7138d0..098ee43f 100644 --- a/data-service/src/test/java/uk/gov/laa/ccms/data/service/ClientServiceTest.java +++ b/data-service/src/test/java/uk/gov/laa/ccms/data/service/ClientServiceTest.java @@ -20,7 +20,7 @@ import uk.gov.laa.ccms.data.repository.TransactionStatusRepository; @ExtendWith(MockitoExtension.class) -@DisplayName("ClientServiceTest") +@DisplayName("Client Service Test") class ClientServiceTest { @Mock @@ -33,7 +33,7 @@ class ClientServiceTest { @BeforeEach void beforeEach() { clientService = - new ClientService(transactionStatusMapper, transactionStatusRepository); + new ClientService(transactionStatusRepository, transactionStatusMapper); } @Test From 715c85a56b6f8a4458e913e0f784c1b05add2bb1 Mon Sep 17 00:00:00 2001 From: Jamie Briggs Date: Wed, 22 Jan 2025 16:13:22 +0000 Subject: [PATCH 08/12] CCMSPUI-382 Endpoint on CaseController implemented, tests tidied up Signed-off-by: Jamie Briggs --- ...rchController.java => CaseController.java} | 17 ++- .../data/controller/ClientsController.java | 8 +- .../data/controller/CaseControllerTest.java | 141 ++++++++++++++++++ .../controller/CaseSearchControllerTest.java | 92 ------------ .../controller/ClientsControllerTest.java | 70 +++++---- 5 files changed, 198 insertions(+), 130 deletions(-) rename data-service/src/main/java/uk/gov/laa/ccms/data/controller/{CaseSearchController.java => CaseController.java} (81%) create mode 100644 data-service/src/test/java/uk/gov/laa/ccms/data/controller/CaseControllerTest.java delete mode 100644 data-service/src/test/java/uk/gov/laa/ccms/data/controller/CaseSearchControllerTest.java diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/controller/CaseSearchController.java b/data-service/src/main/java/uk/gov/laa/ccms/data/controller/CaseController.java similarity index 81% rename from data-service/src/main/java/uk/gov/laa/ccms/data/controller/CaseSearchController.java rename to data-service/src/main/java/uk/gov/laa/ccms/data/controller/CaseController.java index e177312a..db0cee35 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/controller/CaseSearchController.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/controller/CaseController.java @@ -9,24 +9,28 @@ import uk.gov.laa.ccms.data.model.CaseDetails; import uk.gov.laa.ccms.data.model.TransactionStatus; import uk.gov.laa.ccms.data.service.CaseSearchService; +import uk.gov.laa.ccms.data.service.CaseService; +import uk.gov.laa.ccms.data.service.ClientServiceException; /** - * Controller class responsible for handling case search operations. + * Controller class responsible for handling case related operations. * *

This controller serves as an interface to return requested case information. It - * delegates the business logic to the {@link CaseSearchService}.

+ * delegates the business logic to the {@link CaseSearchService} and {@link CaseService}.

* *

This class implemented the {@CasesApi} interface and provides endpoints for retrieving * case information.

* * @see CaseDetails + * @see CaseService * @see CaseSearchService * @author Jamie Briggs */ @RestController @RequiredArgsConstructor -public class CaseSearchController implements CasesApi { +public class CaseController implements CasesApi { + private final CaseService caseService; private final CaseSearchService caseSearchService; /** @@ -57,6 +61,11 @@ public ResponseEntity getCases(Long providerFirmPartyId, String cas @Override public ResponseEntity getCaseTransactionStatus(String transactionRequestId) { - return null; + try { + return caseService.getTransactionStatus(transactionRequestId).map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } catch (ClientServiceException e) { + return ResponseEntity.internalServerError().build(); + } } } diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/controller/ClientsController.java b/data-service/src/main/java/uk/gov/laa/ccms/data/controller/ClientsController.java index 70dbb234..d7cb8a7d 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/controller/ClientsController.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/controller/ClientsController.java @@ -23,10 +23,10 @@ @RestController public class ClientsController implements ClientsApi { - private final ClientService transactionService; + private final ClientService clientService; - public ClientsController(ClientService transactionService) { - this.transactionService = transactionService; + public ClientsController(ClientService clientService) { + this.clientService = clientService; } /** @@ -38,7 +38,7 @@ public ClientsController(ClientService transactionService) { @Override public ResponseEntity getClientTransactionStatus(String transactionRequestId) { try { - return transactionService.getTransactionStatus(transactionRequestId).map(ResponseEntity::ok) + return clientService.getTransactionStatus(transactionRequestId).map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); } catch (ClientServiceException e) { return ResponseEntity.internalServerError().build(); diff --git a/data-service/src/test/java/uk/gov/laa/ccms/data/controller/CaseControllerTest.java b/data-service/src/test/java/uk/gov/laa/ccms/data/controller/CaseControllerTest.java new file mode 100644 index 00000000..a22ee957 --- /dev/null +++ b/data-service/src/test/java/uk/gov/laa/ccms/data/controller/CaseControllerTest.java @@ -0,0 +1,141 @@ +package uk.gov.laa.ccms.data.controller; + +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; + +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.web.PageableHandlerMethodArgumentResolver; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.context.WebApplicationContext; +import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; +import uk.gov.laa.ccms.data.model.CaseDetails; +import uk.gov.laa.ccms.data.model.CaseSummary; +import uk.gov.laa.ccms.data.model.TransactionStatus; +import uk.gov.laa.ccms.data.service.CaseSearchService; +import uk.gov.laa.ccms.data.service.CaseService; +import uk.gov.laa.ccms.data.service.ClientServiceException; + +@ExtendWith({SpringExtension.class}) +@ContextConfiguration +@WebAppConfiguration +@DisplayName("Case Controller Test") +class CaseControllerTest { + + @Mock + private CaseSearchService caseSearchService; + @Mock + private CaseService caseService; + + @InjectMocks + private CaseController caseController; + + private MockMvc mockMvc; + + private ObjectMapper objectMapper; + + @Autowired + WebApplicationContext webApplicationContext; + + @BeforeEach + public void setup() { + mockMvc = standaloneSetup(caseController) + .setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver()) + .build(); + objectMapper = new ObjectMapper(); + } + + @Nested + @DisplayName("getCases() Tests") + class GetCasesTests { + + @Test + @DisplayName("Should return data") + void shouldReturnData() throws Exception { + // Given + CaseSummary caseSummary = new CaseSummary().caseReferenceNumber("123"); + CaseDetails caseDetails = new CaseDetails().addContentItem(caseSummary); + when(caseSearchService.getCases(Mockito.eq(123L), Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(Optional.of(caseDetails)); + // Then + String jsonContent = objectMapper.writeValueAsString(caseDetails); + mockMvc.perform(get("/cases?provider-id=123")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().json(jsonContent)); + } + + @Test + @DisplayName("Should return bad request") + void shouldReturnBadRequest() throws Exception { + // Then + mockMvc.perform(get("/cases")) + .andDo(print()) + .andExpect(status().isBadRequest()); + } + + @Test + @DisplayName("Should return not found") + void shouldReturnNotFound() throws Exception { + // Then + mockMvc.perform(get("/cases?provider-id=123")) + .andDo(print()) + .andExpect(status().isNotFound()); + } + } + + @Nested + @DisplayName("getCaseTransactionStatus() Tests") + class GetCaseTransactionStatusTests { + @Test + @DisplayName("Should return data") + void shouldReturnData() throws Exception { + // Given + TransactionStatus status = new TransactionStatus().submissionStatus("status").referenceNumber("123"); + when(caseService.getTransactionStatus("ABCDEF")).thenReturn(Optional.of(status)); + String jsonContent = objectMapper.writeValueAsString(status); + // Then + mockMvc.perform(get("/cases/status/ABCDEF")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().json(jsonContent)); + } + + @Test + @DisplayName("Should return not found") + void shouldReturnNotFound() throws Exception { + // Then + mockMvc.perform(get("/cases/status/ABCDEF")) + .andDo(print()) + .andExpect(status().isNotFound()); + } + + @Test + @DisplayName("Should return 500 error") + void shouldReturn500Error() throws Exception { + // When + when(caseService.getTransactionStatus("ABCDEF")).thenThrow(new ClientServiceException("error")); + // Then + mockMvc.perform(get("/cases/status/ABCDEF")) + .andDo(print()) + .andExpect(status().is5xxServerError()); + } + } +} diff --git a/data-service/src/test/java/uk/gov/laa/ccms/data/controller/CaseSearchControllerTest.java b/data-service/src/test/java/uk/gov/laa/ccms/data/controller/CaseSearchControllerTest.java deleted file mode 100644 index 6482cfd1..00000000 --- a/data-service/src/test/java/uk/gov/laa/ccms/data/controller/CaseSearchControllerTest.java +++ /dev/null @@ -1,92 +0,0 @@ -package uk.gov.laa.ccms.data.controller; - -import static org.mockito.Mockito.when; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; - -import java.util.Optional; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.web.PageableHandlerMethodArgumentResolver; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.test.context.web.WebAppConfiguration; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.web.context.WebApplicationContext; -import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; -import uk.gov.laa.ccms.data.model.CaseDetails; -import uk.gov.laa.ccms.data.model.CaseSummary; -import uk.gov.laa.ccms.data.service.CaseSearchService; - -@ExtendWith({SpringExtension.class}) -@ContextConfiguration -@WebAppConfiguration -@DisplayName("Case search controller test") -class CaseSearchControllerTest { - - @Mock - private CaseSearchService caseSearchService; - - @InjectMocks - private CaseSearchController caseSearchController; - - private MockMvc mockMvc; - - private ObjectMapper objectMapper; - - @Autowired - WebApplicationContext webApplicationContext; - - @BeforeEach - public void setup() { - mockMvc = standaloneSetup(caseSearchController) - .setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver()) - .build(); - objectMapper = new ObjectMapper(); - } - - @Test - @DisplayName("getCases: Returns data") - void getCases_returnsData() throws Exception { - // Given - CaseSummary caseSummary = new CaseSummary().caseReferenceNumber("123"); - CaseDetails caseDetails = new CaseDetails().addContentItem(caseSummary); - when(caseSearchService.getCases(Mockito.eq(123L), Mockito.any(), - Mockito.any(), Mockito.any(), Mockito.any(), - Mockito.any(), Mockito.any(), Mockito.any())) - .thenReturn(Optional.of(caseDetails)); - // Then - String jsonContent = objectMapper.writeValueAsString(caseDetails); - this.mockMvc.perform(get("/cases?provider-id=123")) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(content().json(jsonContent)); - } - - @Test - @DisplayName("getCases: Returns bad request") - void getCases_returnsBadRequest() throws Exception { - // Then - this.mockMvc.perform(get("/cases")) - .andDo(print()) - .andExpect(status().isBadRequest()); - } - - @Test - @DisplayName("getCases: Returns not found") - void getCases_returnsNotFound() throws Exception { - // Then - this.mockMvc.perform(get("/cases?provider-id=123")) - .andDo(print()) - .andExpect(status().isNotFound()); - } -} diff --git a/data-service/src/test/java/uk/gov/laa/ccms/data/controller/ClientsControllerTest.java b/data-service/src/test/java/uk/gov/laa/ccms/data/controller/ClientsControllerTest.java index c9084635..376f0ab5 100644 --- a/data-service/src/test/java/uk/gov/laa/ccms/data/controller/ClientsControllerTest.java +++ b/data-service/src/test/java/uk/gov/laa/ccms/data/controller/ClientsControllerTest.java @@ -10,6 +10,7 @@ import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -28,6 +29,7 @@ @ExtendWith(MockitoExtension.class) @ContextConfiguration @WebAppConfiguration +@DisplayName("Clients Controller Test") class ClientsControllerTest { @Mock @@ -48,37 +50,45 @@ public void setup(){ objectMapper = new ObjectMapper(); } - @Test - @DisplayName("getClientTransactionStatus() - Returns Data") - void getClientTransactionStatus_returnsData() throws Exception { - // Given - TransactionStatus status = new TransactionStatus().submissionStatus("status").referenceNumber("123"); - when(clientService.getTransactionStatus("ABCDEF")).thenReturn(Optional.of(status)); - String jsonContent = objectMapper.writeValueAsString(status); - // Then - this.mockMvc.perform(get("/clients/status/ABCDEF")) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(content().json(jsonContent)); - } + @Nested + @DisplayName("getClientTransactionStatus() Tests") + class GetClientTransactionStatusTests { - @Test - @DisplayName("getClientTransactionStatus() - Returns not found") - void getClientTransactionStatus_returnsNotFound() throws Exception { - // Then - this.mockMvc.perform(get("/clients/status/ABCDEF")) - .andDo(print()) - .andExpect(status().isNotFound()); - } + @Test + @DisplayName("Should return data") + void shouldReturnData() throws Exception { + // Given + TransactionStatus status = new TransactionStatus().submissionStatus("status") + .referenceNumber("123"); + when(clientService.getTransactionStatus("ABCDEF")).thenReturn(Optional.of(status)); + String jsonContent = objectMapper.writeValueAsString(status); + // Then + mockMvc.perform(get("/clients/status/ABCDEF")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().json(jsonContent)); + } + + @Test + @DisplayName("Should return not found") + void shouldReturnNotFound() throws Exception { + // Then + mockMvc.perform(get("/clients/status/ABCDEF")) + .andDo(print()) + .andExpect(status().isNotFound()); + } + + @Test + @DisplayName("Should return 500 error") + void shouldReturn500Error() throws Exception { + // When + when(clientService.getTransactionStatus("ABCDEF")).thenThrow( + new ClientServiceException("error")); + // Then + mockMvc.perform(get("/clients/status/ABCDEF")) + .andDo(print()) + .andExpect(status().is5xxServerError()); + } - @Test - @DisplayName("getClientTransactionStatus() - Returns 500 error") - void getClientTransactionStatus_returns500Error() throws Exception { - // When - when(clientService.getTransactionStatus("ABCDEF")).thenThrow(new ClientServiceException("error")); - // Then - this.mockMvc.perform(get("/clients/status/ABCDEF")) - .andDo(print()) - .andExpect(status().is5xxServerError()); } } From 035de43df8905f308852bebd6700f0f2a02b817c Mon Sep 17 00:00:00 2001 From: Jamie Briggs Date: Wed, 22 Jan 2025 19:14:36 +0000 Subject: [PATCH 09/12] CCMSPUI-382 Added javadocs to CaseService Signed-off-by: Jamie Briggs --- .../laa/ccms/data/service/CaseService.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/service/CaseService.java b/data-service/src/main/java/uk/gov/laa/ccms/data/service/CaseService.java index 0f589789..ad74c0bf 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/service/CaseService.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/service/CaseService.java @@ -8,6 +8,17 @@ import uk.gov.laa.ccms.data.model.TransactionStatus; import uk.gov.laa.ccms.data.repository.TransactionStatusRepository; +/** + * Service class responsible for handling case-related operations. + * + *

This class provides methods to retrieve transaction statuses for case-related + * transactions. It utilizes a repository for database access and a mapper for converting + * database entities to objects used in the application.

+ * + * @see TransactionStatusRepository + * @see TransactionStatusMapper + * @author Jamie Briggs + */ @Service @RequiredArgsConstructor public class CaseService { @@ -15,7 +26,17 @@ public class CaseService { private final TransactionStatusRepository transactionStatusRepository; private final TransactionStatusMapper transactionStatusMapper; - + /** + * Retrieves the transaction status for a given transaction ID. If the transaction + * is not found, an empty {@code Optional} is returned. + * + * @param transactionId the unique identifier of the transaction whose status + * is to be retrieved + * @return an {@code Optional} containing the {@link TransactionStatus} if found, + * or an empty {@code Optional} if not found + * @throws ClientServiceException throws exception when there was an error found + * with the associated transaction ID + */ public Optional getTransactionStatus(String transactionId) { List userFuncStatus = transactionStatusRepository.findAllUserFunctionTransactionsByTransactionId(transactionId); From f8a8014b501aaa2400933c66d3b7bf5254a2b906 Mon Sep 17 00:00:00 2001 From: Jamie Briggs Date: Fri, 24 Jan 2025 12:28:35 +0000 Subject: [PATCH 10/12] Fixed ID issue where some rows had similar request IDs, timestamps, but different process names. Signed-off-by: Jamie Briggs --- .../ccms/data/entity/TransactionStatus.java | 21 +++++++++++++++++-- .../TransactionStatusRepository.java | 5 ++++- .../laa/ccms/data/service/ClientService.java | 2 ++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java index 82b7d586..bd808261 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/TransactionStatus.java @@ -8,9 +8,9 @@ import jakarta.persistence.Table; import java.io.Serializable; import java.time.LocalDateTime; +import java.util.Objects; import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; @@ -42,6 +42,7 @@ public class TransactionStatus { @Column(name = "TRANSACTION_OCCURRENCE_DATE") private LocalDateTime transactionOccurrenceDate; + @Id @Column(name = "PROCESS_NAME", length = 50) private String processName; @@ -65,9 +66,25 @@ public class TransactionStatus { */ @Getter @Setter - @EqualsAndHashCode public static class TransactionStatusId implements Serializable { private String requestId; + private String processName; private LocalDateTime transactionOccurrenceDate; + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + TransactionStatusId that = (TransactionStatusId) o; + return Objects.equals(requestId, that.requestId) && Objects.equals( + processName, that.processName) && Objects.equals(transactionOccurrenceDate, + that.transactionOccurrenceDate); + } + + @Override + public int hashCode() { + return Objects.hash(requestId, processName, transactionOccurrenceDate); + } } } diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java index d903327d..f424589c 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java @@ -5,6 +5,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import uk.gov.laa.ccms.data.entity.TransactionStatus; +import uk.gov.laa.ccms.data.entity.TransactionStatus.TransactionStatusId; /** * Repository interface for accessing {@link TransactionStatus} entities. @@ -17,7 +18,7 @@ * @author Jamie Briggs */ @Repository -public interface TransactionStatusRepository extends ReadOnlyRepository { +public interface TransactionStatusRepository extends ReadOnlyRepository { @Query(""" SELECT ts FROM TransactionStatus ts @@ -40,6 +41,8 @@ public interface TransactionStatusRepository extends ReadOnlyRepository findClientTransactionByTransactionId(String transactionId); + List findAllByRequestId(String requestId); + /** * Finds a case transaction with a specific transaction ID. * diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java b/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java index c0cc7ab5..b0c8df92 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java @@ -45,6 +45,8 @@ public Optional getTransactionStatus(String transactionId) .equalsIgnoreCase("ERROR"))) { throw new ClientServiceException("Error found in user function"); } + List all = + transactionStatusRepository.findAllByRequestId(transactionId); return transactionStatusRepository.findClientTransactionByTransactionId(transactionId) .map(transactionStatusMapper::toTransactionStatus); } From 6cba48576e255ba6ba3535d13dc63631c75bdcc0 Mon Sep 17 00:00:00 2001 From: Jamie Briggs Date: Fri, 24 Jan 2025 12:32:14 +0000 Subject: [PATCH 11/12] Fixed ID issue where some rows had similar request IDs, timestamps, but different process names. Signed-off-by: Jamie Briggs --- .../main/java/uk/gov/laa/ccms/data/service/ClientService.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java b/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java index b0c8df92..c0cc7ab5 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/service/ClientService.java @@ -45,8 +45,6 @@ public Optional getTransactionStatus(String transactionId) .equalsIgnoreCase("ERROR"))) { throw new ClientServiceException("Error found in user function"); } - List all = - transactionStatusRepository.findAllByRequestId(transactionId); return transactionStatusRepository.findClientTransactionByTransactionId(transactionId) .map(transactionStatusMapper::toTransactionStatus); } From 362bac59260ac3fab7fc5f64bbee0ad8177d91f8 Mon Sep 17 00:00:00 2001 From: Jamie Briggs Date: Fri, 24 Jan 2025 12:37:51 +0000 Subject: [PATCH 12/12] Checkstyle issue resolved Signed-off-by: Jamie Briggs --- .../laa/ccms/data/repository/TransactionStatusRepository.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java index ee4517fc..f0a177cc 100644 --- a/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/TransactionStatusRepository.java @@ -18,7 +18,8 @@ * @author Jamie Briggs */ @Repository -public interface TransactionStatusRepository extends ReadOnlyRepository { +public interface TransactionStatusRepository + extends ReadOnlyRepository { @Query(""" SELECT ts FROM TransactionStatus ts