From 95e6f2df118b09e3cf9c4c3799cabbf3497e7830 Mon Sep 17 00:00:00 2001 From: Russell Dodd Date: Thu, 2 May 2024 08:56:39 +0100 Subject: [PATCH] Add LJA Reference Data Endpoint & infrastructure code --- ...lJusticeAreaControllerIntegrationTest.java | 2 +- .../controllers/BusinessUnitController.java | 2 +- .../LocalJusticeAreaController.java | 22 ++++++- .../opal/controllers/OffenceController.java | 2 +- .../reference/LjaReferenceDataResults.java | 32 ++++++++++ .../dto/search/LocalJusticeAreaSearchDto.java | 2 +- .../opal/entity/LocalJusticeAreaEntity.java | 17 ++++++ .../entity/projection/LjaReferenceData.java | 14 +++++ .../repository/jpa/LocalJusticeAreaSpecs.java | 30 ++++++++++ .../service/opal/LocalJusticeAreaService.java | 17 ++++++ .../LocalJusticeAreaControllerTest.java | 58 ++++++++++++++++++- .../opal/LocalJusticeAreaServiceTest.java | 24 ++++++++ 12 files changed, 214 insertions(+), 8 deletions(-) rename src/integrationTest/java/uk/gov/hmcts/opal/controllers/{develop => }/LocalJusticeAreaControllerIntegrationTest.java (99%) rename src/main/java/uk/gov/hmcts/opal/controllers/{develop => }/LocalJusticeAreaController.java (67%) create mode 100644 src/main/java/uk/gov/hmcts/opal/dto/reference/LjaReferenceDataResults.java create mode 100644 src/main/java/uk/gov/hmcts/opal/entity/projection/LjaReferenceData.java rename src/test/java/uk/gov/hmcts/opal/controllers/{develop => }/LocalJusticeAreaControllerTest.java (57%) diff --git a/src/integrationTest/java/uk/gov/hmcts/opal/controllers/develop/LocalJusticeAreaControllerIntegrationTest.java b/src/integrationTest/java/uk/gov/hmcts/opal/controllers/LocalJusticeAreaControllerIntegrationTest.java similarity index 99% rename from src/integrationTest/java/uk/gov/hmcts/opal/controllers/develop/LocalJusticeAreaControllerIntegrationTest.java rename to src/integrationTest/java/uk/gov/hmcts/opal/controllers/LocalJusticeAreaControllerIntegrationTest.java index 180b2540f..deefea6be 100644 --- a/src/integrationTest/java/uk/gov/hmcts/opal/controllers/develop/LocalJusticeAreaControllerIntegrationTest.java +++ b/src/integrationTest/java/uk/gov/hmcts/opal/controllers/LocalJusticeAreaControllerIntegrationTest.java @@ -1,4 +1,4 @@ -package uk.gov.hmcts.opal.controllers.develop; +package uk.gov.hmcts.opal.controllers; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/main/java/uk/gov/hmcts/opal/controllers/BusinessUnitController.java b/src/main/java/uk/gov/hmcts/opal/controllers/BusinessUnitController.java index 66e7b6103..bc42356c1 100644 --- a/src/main/java/uk/gov/hmcts/opal/controllers/BusinessUnitController.java +++ b/src/main/java/uk/gov/hmcts/opal/controllers/BusinessUnitController.java @@ -64,7 +64,7 @@ public ResponseEntity> postBusinessUnitsSearch( } @GetMapping(value = {"/ref-data", "/ref-data/", "/ref-data/{filter}"}) - @Operation(summary = "Returns Business Units as reference data with an option filter applied") + @Operation(summary = "Returns Business Units as reference data with an optional filter applied") public ResponseEntity getBusinessUnitRefData( @PathVariable Optional filter) { log.info(":GET:getBusinessUnitRefData: query: \n{}", filter); diff --git a/src/main/java/uk/gov/hmcts/opal/controllers/develop/LocalJusticeAreaController.java b/src/main/java/uk/gov/hmcts/opal/controllers/LocalJusticeAreaController.java similarity index 67% rename from src/main/java/uk/gov/hmcts/opal/controllers/develop/LocalJusticeAreaController.java rename to src/main/java/uk/gov/hmcts/opal/controllers/LocalJusticeAreaController.java index ef1fe124e..1e3cf97ff 100644 --- a/src/main/java/uk/gov/hmcts/opal/controllers/develop/LocalJusticeAreaController.java +++ b/src/main/java/uk/gov/hmcts/opal/controllers/LocalJusticeAreaController.java @@ -1,4 +1,4 @@ -package uk.gov.hmcts.opal.controllers.develop; +package uk.gov.hmcts.opal.controllers; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -12,11 +12,15 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import uk.gov.hmcts.opal.dto.reference.LjaReferenceDataResults; import uk.gov.hmcts.opal.dto.search.LocalJusticeAreaSearchDto; import uk.gov.hmcts.opal.entity.LocalJusticeAreaEntity; +import uk.gov.hmcts.opal.entity.projection.LjaReferenceData; import uk.gov.hmcts.opal.service.LocalJusticeAreaServiceInterface; +import uk.gov.hmcts.opal.service.opal.LocalJusticeAreaService; import java.util.List; +import java.util.Optional; import static uk.gov.hmcts.opal.util.HttpUtil.buildResponse; @@ -29,9 +33,13 @@ public class LocalJusticeAreaController { private final LocalJusticeAreaServiceInterface localJusticeAreaService; + private final LocalJusticeAreaService opalLocalJusticeAreaService; + public LocalJusticeAreaController( - @Qualifier("localJusticeAreaService") LocalJusticeAreaServiceInterface localJusticeAreaService) { + @Qualifier("localJusticeAreaService") LocalJusticeAreaServiceInterface localJusticeAreaService, + LocalJusticeAreaService opalLocalJusticeAreaService) { this.localJusticeAreaService = localJusticeAreaService; + this.opalLocalJusticeAreaService = opalLocalJusticeAreaService; } @GetMapping(value = "/{localJusticeAreaId}") @@ -56,5 +64,15 @@ public ResponseEntity> postLocalJusticeAreasSearch( return buildResponse(response); } + @GetMapping(value = {"/ref-data", "/ref-data/", "/ref-data/{filter}"}) + @Operation(summary = "Returns Local Justice Area as reference data with an optional filter applied") + public ResponseEntity getLocalJusticeAreaRefData( + @PathVariable Optional filter) { + log.info(":GET:getLocalJusticeAreaRefData: query: \n{}", filter); + + List refData = opalLocalJusticeAreaService.getReferenceData(filter); + log.info(":GET:getLocalJusticeAreaRefData: local justice area reference data count: {}", refData.size()); + return ResponseEntity.ok(LjaReferenceDataResults.builder().refData(refData).build()); + } } diff --git a/src/main/java/uk/gov/hmcts/opal/controllers/OffenceController.java b/src/main/java/uk/gov/hmcts/opal/controllers/OffenceController.java index b9915b880..c486015f0 100644 --- a/src/main/java/uk/gov/hmcts/opal/controllers/OffenceController.java +++ b/src/main/java/uk/gov/hmcts/opal/controllers/OffenceController.java @@ -62,7 +62,7 @@ public ResponseEntity> postOffencesSearch(@RequestBody Offen } @GetMapping(value = {"/ref-data", "/ref-data/", "/ref-data/{filter}"}) - @Operation(summary = "Returns Offences as reference data with an option filter applied") + @Operation(summary = "Returns Offences as reference data with an optional filter applied") public ResponseEntity getOffenceRefData(@PathVariable Optional filter) { log.info(":GET:getOffenceRefData: query: \n{}", filter); diff --git a/src/main/java/uk/gov/hmcts/opal/dto/reference/LjaReferenceDataResults.java b/src/main/java/uk/gov/hmcts/opal/dto/reference/LjaReferenceDataResults.java new file mode 100644 index 000000000..d0d1a617e --- /dev/null +++ b/src/main/java/uk/gov/hmcts/opal/dto/reference/LjaReferenceDataResults.java @@ -0,0 +1,32 @@ +package uk.gov.hmcts.opal.dto.reference; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import uk.gov.hmcts.opal.entity.projection.LjaReferenceData; + +import java.util.List; +import java.util.Optional; + +@Builder +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class LjaReferenceDataResults { + private Integer count; + private List refData; + + public static class LjaReferenceDataResultsBuilder { + public LjaReferenceDataResultsBuilder refData( + List refData) { + this.refData = refData; + return this.count(Optional.ofNullable(refData).map(List::size).orElse(0)); + } + + private LjaReferenceDataResultsBuilder count(Integer count) { + this.count = count; + return this; + } + } +} diff --git a/src/main/java/uk/gov/hmcts/opal/dto/search/LocalJusticeAreaSearchDto.java b/src/main/java/uk/gov/hmcts/opal/dto/search/LocalJusticeAreaSearchDto.java index 01c70174f..7b170802f 100644 --- a/src/main/java/uk/gov/hmcts/opal/dto/search/LocalJusticeAreaSearchDto.java +++ b/src/main/java/uk/gov/hmcts/opal/dto/search/LocalJusticeAreaSearchDto.java @@ -17,5 +17,5 @@ public class LocalJusticeAreaSearchDto extends AddressSearch implements ToJsonString { private String localJusticeAreaId; - + private String ljaCode; } diff --git a/src/main/java/uk/gov/hmcts/opal/entity/LocalJusticeAreaEntity.java b/src/main/java/uk/gov/hmcts/opal/entity/LocalJusticeAreaEntity.java index b15af6be4..7e80473b3 100644 --- a/src/main/java/uk/gov/hmcts/opal/entity/LocalJusticeAreaEntity.java +++ b/src/main/java/uk/gov/hmcts/opal/entity/LocalJusticeAreaEntity.java @@ -10,12 +10,16 @@ import jakarta.persistence.Id; import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; +import java.time.LocalDateTime; + @Data @Entity @EqualsAndHashCode(callSuper = true) @@ -34,4 +38,17 @@ public class LocalJusticeAreaEntity extends AddressEntity { @Column(name = "local_justice_area_id", nullable = false) private Short localJusticeAreaId; + @Column(name = "lja_code", length = 4) + private String ljaCode; + + @Column(name = "address_line_4", length = 35) + private String addressLine4; + + @Column(name = "address_line_5", length = 35) + private String addressLine5; + + @Column(name = "end_date") + @Temporal(TemporalType.TIMESTAMP) + private LocalDateTime endDate; + } diff --git a/src/main/java/uk/gov/hmcts/opal/entity/projection/LjaReferenceData.java b/src/main/java/uk/gov/hmcts/opal/entity/projection/LjaReferenceData.java new file mode 100644 index 000000000..b220b098d --- /dev/null +++ b/src/main/java/uk/gov/hmcts/opal/entity/projection/LjaReferenceData.java @@ -0,0 +1,14 @@ +package uk.gov.hmcts.opal.entity.projection; + +public interface LjaReferenceData { + + Short getLocalJusticeAreaId(); + + String getLjaCode(); + + String getName(); + + String getAddressLine1(); + + String getPostcode(); +} diff --git a/src/main/java/uk/gov/hmcts/opal/repository/jpa/LocalJusticeAreaSpecs.java b/src/main/java/uk/gov/hmcts/opal/repository/jpa/LocalJusticeAreaSpecs.java index 572a66cd2..7baa8365d 100644 --- a/src/main/java/uk/gov/hmcts/opal/repository/jpa/LocalJusticeAreaSpecs.java +++ b/src/main/java/uk/gov/hmcts/opal/repository/jpa/LocalJusticeAreaSpecs.java @@ -5,18 +5,48 @@ import uk.gov.hmcts.opal.entity.LocalJusticeAreaEntity; import uk.gov.hmcts.opal.entity.LocalJusticeAreaEntity_; +import java.time.LocalDateTime; +import java.util.Optional; + public class LocalJusticeAreaSpecs extends AddressSpecs { public Specification findBySearchCriteria(LocalJusticeAreaSearchDto criteria) { return Specification.allOf(specificationList( findByAddressCriteria(criteria), + notBlank(criteria.getLjaCode()).map(LocalJusticeAreaSpecs::likeLjaCode), notBlank(criteria.getLocalJusticeAreaId()).map(LocalJusticeAreaSpecs::equalsLocalJusticeAreaId) )); } + public Specification referenceDataFilter(Optional filter) { + return Specification.allOf(specificationList( + Optional.of(LocalDateTime.now()).map(LocalJusticeAreaSpecs::endDateGreaterThenEqualToDate), + filter.filter(s -> !s.isBlank()).map(this::likeAnyLocalJusticeArea) + )); + } + public static Specification equalsLocalJusticeAreaId(String localJusticeAreaId) { return (root, query, builder) -> builder.equal(root.get(LocalJusticeAreaEntity_.localJusticeAreaId), localJusticeAreaId); } + public static Specification likeLjaCode(String ljaCode) { + return (root, query, builder) -> + likeWildcardPredicate(root.get(LocalJusticeAreaEntity_.ljaCode), builder, ljaCode); + } + + public Specification likeAnyLocalJusticeArea(String filter) { + return Specification.anyOf( + likeLjaCode(filter), + likeName(filter), + likePostcode(filter) + ); + } + + public static Specification endDateGreaterThenEqualToDate(LocalDateTime expiryDate) { + return (root, query, builder) -> builder.or( + builder.isNull(root.get(LocalJusticeAreaEntity_.endDate)), + builder.greaterThanOrEqualTo(root.get(LocalJusticeAreaEntity_.endDate), expiryDate) + ); + } } diff --git a/src/main/java/uk/gov/hmcts/opal/service/opal/LocalJusticeAreaService.java b/src/main/java/uk/gov/hmcts/opal/service/opal/LocalJusticeAreaService.java index a1ab7f850..913ea7874 100644 --- a/src/main/java/uk/gov/hmcts/opal/service/opal/LocalJusticeAreaService.java +++ b/src/main/java/uk/gov/hmcts/opal/service/opal/LocalJusticeAreaService.java @@ -5,14 +5,18 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import uk.gov.hmcts.opal.dto.search.LocalJusticeAreaSearchDto; import uk.gov.hmcts.opal.entity.LocalJusticeAreaEntity; +import uk.gov.hmcts.opal.entity.LocalJusticeAreaEntity_; +import uk.gov.hmcts.opal.entity.projection.LjaReferenceData; import uk.gov.hmcts.opal.repository.LocalJusticeAreaRepository; import uk.gov.hmcts.opal.repository.jpa.LocalJusticeAreaSpecs; import uk.gov.hmcts.opal.service.LocalJusticeAreaServiceInterface; import java.util.List; +import java.util.Optional; @Service @RequiredArgsConstructor @@ -37,4 +41,17 @@ public List searchLocalJusticeAreas(LocalJusticeAreaSear return page.getContent(); } + public List getReferenceData(Optional filter) { + + Sort nameSort = Sort.by(Sort.Direction.ASC, LocalJusticeAreaEntity_.NAME); + + Page page = localJusticeAreaRepository + .findBy(specs.referenceDataFilter(filter), + ffq -> ffq + .sortBy(nameSort) + .as(LjaReferenceData.class) + .page(Pageable.unpaged())); + + return page.getContent(); + } } diff --git a/src/test/java/uk/gov/hmcts/opal/controllers/develop/LocalJusticeAreaControllerTest.java b/src/test/java/uk/gov/hmcts/opal/controllers/LocalJusticeAreaControllerTest.java similarity index 57% rename from src/test/java/uk/gov/hmcts/opal/controllers/develop/LocalJusticeAreaControllerTest.java rename to src/test/java/uk/gov/hmcts/opal/controllers/LocalJusticeAreaControllerTest.java index 8102467b6..714858798 100644 --- a/src/test/java/uk/gov/hmcts/opal/controllers/develop/LocalJusticeAreaControllerTest.java +++ b/src/test/java/uk/gov/hmcts/opal/controllers/LocalJusticeAreaControllerTest.java @@ -1,4 +1,4 @@ -package uk.gov.hmcts.opal.controllers.develop; +package uk.gov.hmcts.opal.controllers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -7,12 +7,14 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import uk.gov.hmcts.opal.controllers.develop.LocalJusticeAreaController; +import uk.gov.hmcts.opal.dto.reference.LjaReferenceDataResults; import uk.gov.hmcts.opal.dto.search.LocalJusticeAreaSearchDto; import uk.gov.hmcts.opal.entity.LocalJusticeAreaEntity; +import uk.gov.hmcts.opal.entity.projection.LjaReferenceData; import uk.gov.hmcts.opal.service.opal.LocalJusticeAreaService; import java.util.List; +import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; @@ -65,4 +67,56 @@ void testSearchLocalJusticeAreas_Success() { verify(localJusticeAreaService, times(1)).searchLocalJusticeAreas(any()); } + + @Test + void testGetLocalJusticeAreasRefData_Success() { + // Arrange + LjaReferenceData entity = createLjaReferenceData(); + List localJusticeAreaList = List.of(entity); + + when(localJusticeAreaService.getReferenceData(any())).thenReturn(localJusticeAreaList); + + // Act + Optional filter = Optional.empty(); + ResponseEntity response = localJusticeAreaController + .getLocalJusticeAreaRefData(filter); + + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + LjaReferenceDataResults refDataResults = response.getBody(); + assertEquals(1, refDataResults.getCount()); + assertEquals(localJusticeAreaList, refDataResults.getRefData()); + verify(localJusticeAreaService, times(1)).getReferenceData(any()); + } + + private LjaReferenceData createLjaReferenceData() { + return new LjaReferenceData() { + + @Override + public Short getLocalJusticeAreaId() { + return (short)1; + } + + @Override + public String getLjaCode() { + return "MAIN"; + } + + @Override + public String getName() { + return "Local Justice Area Main"; + } + + @Override + public String getAddressLine1() { + return "No.1 The Old Bailey"; + } + + @Override + public String getPostcode() { + return "BB1 1BB"; + } + }; + } + } diff --git a/src/test/java/uk/gov/hmcts/opal/service/opal/LocalJusticeAreaServiceTest.java b/src/test/java/uk/gov/hmcts/opal/service/opal/LocalJusticeAreaServiceTest.java index 91bfc4e43..56897f526 100644 --- a/src/test/java/uk/gov/hmcts/opal/service/opal/LocalJusticeAreaServiceTest.java +++ b/src/test/java/uk/gov/hmcts/opal/service/opal/LocalJusticeAreaServiceTest.java @@ -13,9 +13,11 @@ import org.springframework.data.repository.query.FluentQuery; import uk.gov.hmcts.opal.dto.search.LocalJusticeAreaSearchDto; import uk.gov.hmcts.opal.entity.LocalJusticeAreaEntity; +import uk.gov.hmcts.opal.entity.projection.LjaReferenceData; import uk.gov.hmcts.opal.repository.LocalJusticeAreaRepository; import java.util.List; +import java.util.Optional; import java.util.function.Function; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -70,5 +72,27 @@ void testSearchLocalJusticeAreas() { } + @SuppressWarnings("unchecked") + @Test + void testLocalJusticeAreasReferenceData() { + // Arrange + FluentQuery.FetchableFluentQuery ffq = Mockito.mock(FluentQuery.FetchableFluentQuery.class); + when(ffq.as(any())).thenReturn(ffq); + when(ffq.sortBy(any())).thenReturn(ffq); + + LocalJusticeAreaEntity localJAEntity = LocalJusticeAreaEntity.builder().build(); + Page mockPage = new PageImpl<>(List.of(localJAEntity), Pageable.unpaged(), 999L); + when(localJusticeAreaRepository.findBy(any(Specification.class), any())).thenAnswer(iom -> { + iom.getArgument(1, Function.class).apply(ffq); + return mockPage; + }); + + // Act + List result = localJusticeAreaService.getReferenceData(Optional.empty()); + + // Assert + assertEquals(List.of(localJAEntity), result); + + } }