From bfcd63d7eccabd2ba78a774409b6d6df45e76546 Mon Sep 17 00:00:00 2001
From: Josh <23316525+josh-uk@users.noreply.github.com>
Date: Wed, 17 Jun 2026 14:49:03 +0100
Subject: [PATCH 01/40] FACT-2618 Remove old service centre code (#271)
Remove old service centre code
---
.../controllers/AuthFunctionalTest.java | 3 -
.../CourtControllerFunctionalTest.java | 14 ---
.../SearchCourtControllerFunctionalTest.java | 5 +-
...chServiceAreaControllerFunctionalTest.java | 68 ----------
.../functional/helpers/TestDataHelper.java | 2 -
.../fact/data/api/audit/AuditingTest.java | 1 -
.../api/controllers/AuditControllerTest.java | 1 -
.../api/controllers/CourtControllerTest.java | 2 -
.../SearchServiceAreaControllerTest.java | 69 ----------
.../data/api/db/CourtLastUpdatedTimeTest.java | 1 -
.../controllers/TestingSupportController.java | 4 -
.../search/SearchServiceAreaController.java | 46 -------
.../api/entities/AbstractCourtEntity.java | 4 -
.../data/api/entities/CourtServiceAreas.java | 91 --------------
.../api/entities/types/SearchStrategy.java | 2 -
.../service/CourtMigrationHelper.java | 77 ++----------
.../migration/service/MigrationContext.java | 6 -
.../migration/service/MigrationService.java | 6 +-
.../repositories/CourtAddressRepository.java | 89 -------------
.../CourtServiceAreasRepository.java | 53 --------
.../fact/data/api/services/CourtService.java | 2 +-
.../api/services/CourtServiceAreaService.java | 56 ---------
.../data/api/services/ServiceAreaService.java | 19 +--
.../api/services/TestingSupportService.java | 53 ++------
.../services/search/SearchCourtService.java | 12 +-
.../api/services/search/SearchExecuter.java | 48 -------
...7__drop_service_centre_data_structures.sql | 9 ++
.../AuditableCourtEntityListenerTest.java | 1 -
.../api/controllers/CourtControllerTest.java | 1 -
.../TestingSupportControllerTest.java | 16 +--
.../SearchServiceAreaControllerTest.java | 42 -------
.../service/CourtMigrationHelperTest.java | 25 +---
.../service/MigrationServiceTest.java | 19 +--
.../services/CourtServiceAreaServiceTest.java | 119 ------------------
.../api/services/SearchCourtServiceTest.java | 31 +----
.../api/services/ServiceAreaServiceTest.java | 4 -
.../services/TestingSupportServiceTest.java | 84 +++----------
.../services/search/SearchExecuterTest.java | 92 --------------
38 files changed, 64 insertions(+), 1113 deletions(-)
delete mode 100644 src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/search/SearchServiceAreaControllerFunctionalTest.java
delete mode 100644 src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/search/SearchServiceAreaControllerTest.java
delete mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/search/SearchServiceAreaController.java
delete mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/CourtServiceAreas.java
delete mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/CourtServiceAreasRepository.java
delete mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/services/CourtServiceAreaService.java
create mode 100644 src/main/resources/db/migration/V1.27__drop_service_centre_data_structures.sql
delete mode 100644 src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/search/SearchServiceAreaControllerTest.java
delete mode 100644 src/test/java/uk/gov/hmcts/reform/fact/data/api/services/CourtServiceAreaServiceTest.java
diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/AuthFunctionalTest.java b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/AuthFunctionalTest.java
index e9ab2ebbd..4373eebf7 100644
--- a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/AuthFunctionalTest.java
+++ b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/AuthFunctionalTest.java
@@ -77,7 +77,6 @@ private static UUID createCourtAsAdmin(String name) {
court.setName(TestDataHelper.appendRandomSuffixToCourtName(name));
court.setRegionId(UUID.fromString(getRegionId()));
court.setOpen(Boolean.FALSE);
- court.setIsServiceCentre(true);
Response createResponse = http.doPost("/courts/v1", court, adminToken);
assertThat(createResponse.statusCode()).isEqualTo(201);
return UUID.fromString(createResponse.jsonPath().getString("id"));
@@ -159,7 +158,6 @@ void courtControllerAuth() {
testingCourt.setName(TestDataHelper.appendRandomSuffixToCourtName("Test Court Auth"));
testingCourt.setOpen(Boolean.FALSE);
testingCourt.setRegionId(UUID.fromString(getRegionId()));
- testingCourt.setIsServiceCentre(true);
// initially test that only the admin can create the court
Response createViewer = http.doPost("/courts/v1", testingCourt, viewerToken);
@@ -637,7 +635,6 @@ void searchEndpointsAuth() {
"/search/courts/v1/postcode?postcode=SW1A1AA&limit=5", "courts by postcode",
"/search/courts/v1/prefix?prefix=A", "courts by prefix",
"/search/courts/v1/name?q=court", "courts by name",
- "/search/service-area/v1/adoption", "service area",
"/search/services/v1", "services",
"/search/services/v1/divorce/service-areas", "service areas by service"
);
diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/CourtControllerFunctionalTest.java b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/CourtControllerFunctionalTest.java
index 3c949102a..e926d22ea 100644
--- a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/CourtControllerFunctionalTest.java
+++ b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/CourtControllerFunctionalTest.java
@@ -44,7 +44,6 @@ void shouldCreateCourtWithValidPayload() throws Exception {
final Court court = new Court();
court.setName(TestDataHelper.appendRandomSuffixToCourtName("Test Court Create Valid"));
court.setRegionId(UUID.fromString(regionId));
- court.setIsServiceCentre(true);
court.setOpen(false);
final Response createResponse = http.doPost("/courts/v1", court);
@@ -67,9 +66,6 @@ void shouldCreateCourtWithValidPayload() throws Exception {
assertThat(fetchedCourt.getRegionId())
.as("Court region ID should match")
.isEqualTo(UUID.fromString(regionId));
- assertThat(fetchedCourt.getIsServiceCentre())
- .as("Court should be a service centre")
- .isTrue();
}
@Test
@@ -78,7 +74,6 @@ void shouldFailWithNonExistentRegionId() throws Exception {
final Court court = new Court();
court.setName(TestDataHelper.appendRandomSuffixToCourtName("Test Court Invalid Region"));
court.setRegionId(UUID.randomUUID());
- court.setIsServiceCentre(true);
court.setOpen(false);
final Response response = http.doPost("/courts/v1", court);
@@ -95,7 +90,6 @@ void shouldFailWithNonExistentRegionId() throws Exception {
void shouldFailWithMissingName() throws Exception {
final Court court = new Court();
court.setRegionId(UUID.fromString(regionId));
- court.setIsServiceCentre(true);
final Response response = http.doPost("/courts/v1", court);
@@ -130,7 +124,6 @@ void shouldUpdateExistingCourt() throws Exception {
final Court updatedCourt = new Court();
updatedCourt.setName(TestDataHelper.appendRandomSuffixToCourtName("Test Court Updated"));
updatedCourt.setRegionId(UUID.fromString(regionId));
- updatedCourt.setIsServiceCentre(true);
updatedCourt.setOpen(false);
final Response updateResponse = http.doPut("/courts/" + courtId + "/v1", updatedCourt);
@@ -145,10 +138,6 @@ void shouldUpdateExistingCourt() throws Exception {
assertThat(fetchedCourt.getName())
.as("Court name should be updated")
.startsWith("Test Court Updated");
- assertThat(fetchedCourt.getIsServiceCentre())
- .as("Court should remain a service centre")
- .isTrue();
-
final ZonedDateTime timestampAfterUpdate = AssertionHelper.getCourtLastUpdatedAt(http, courtId);
assertThat(timestampAfterUpdate)
.as("Court lastUpdatedAt should move forward after court update for court %s", courtId)
@@ -163,7 +152,6 @@ void shouldFailToUpdateNonExistentCourt() throws Exception {
final Court updatedCourt = new Court();
updatedCourt.setName(TestDataHelper.appendRandomSuffixToCourtName("Test Court Non-Existent court ID"));
updatedCourt.setRegionId(UUID.fromString(regionId));
- updatedCourt.setIsServiceCentre(true);
updatedCourt.setOpen(false);
final Response response = http.doPut("/courts/" + nonExistentCourtId + "/v1", updatedCourt);
@@ -183,7 +171,6 @@ void shouldFailToUpdateCourtWithNonExistentRegionId() throws Exception {
final Court updatedCourt = new Court();
updatedCourt.setName(TestDataHelper.appendRandomSuffixToCourtName("Test Court Non-Existent Region ID Updated"));
updatedCourt.setRegionId(UUID.randomUUID());
- updatedCourt.setIsServiceCentre(true);
updatedCourt.setOpen(false);
final Response response = http.doPut("/courts/" + courtId + "/v1", updatedCourt);
@@ -231,7 +218,6 @@ void shouldReturnOnlyActiveCourts() throws Exception {
final Court updatedCourt = new Court();
updatedCourt.setName(TestDataHelper.appendRandomSuffixToCourtName("Test Court Open"));
updatedCourt.setRegionId(UUID.fromString(regionId));
- updatedCourt.setIsServiceCentre(true);
updatedCourt.setOpen(true);
final Response updateResponse = http.doPut("/courts/" + courtId + "/v1", updatedCourt);
diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/search/SearchCourtControllerFunctionalTest.java b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/search/SearchCourtControllerFunctionalTest.java
index 7cc472d3c..ca63ac9ec 100644
--- a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/search/SearchCourtControllerFunctionalTest.java
+++ b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/search/SearchCourtControllerFunctionalTest.java
@@ -159,9 +159,7 @@ void shouldReturnNearestCourtsForPostcodeOnlySearch() throws Exception {
* - Childcare SPOE special-case (single point of entry search)
*
*
- * The only search strategy not covered is FAMILY_REGIONAL, because there is currently no API endpoint to
- * link courts to service areas (court_service_areas). Once an endpoint is available, a functional test
- * should be added to cover the FAMILY_REGIONAL flow end-to-end.
+ * TODO: Add service-centre search coverage when that model is introduced.
*/
@Test
@DisplayName("GET /search/courts/v1/postcode returns courts for OTHER type service area using DEFAULT_AOL_DISTANCE")
@@ -652,7 +650,6 @@ private static UUID createOpenCourt(final String courtName) {
final Court courtToUpdate = new Court();
courtToUpdate.setName(TestDataHelper.appendRandomSuffixToCourtName(courtName));
courtToUpdate.setRegionId(UUID.fromString(regionId));
- courtToUpdate.setIsServiceCentre(true);
courtToUpdate.setOpen(true);
final Response updateResponse = http.doPut("/courts/" + courtId + "/v1", courtToUpdate);
diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/search/SearchServiceAreaControllerFunctionalTest.java b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/search/SearchServiceAreaControllerFunctionalTest.java
deleted file mode 100644
index 7ad82c13b..000000000
--- a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/search/SearchServiceAreaControllerFunctionalTest.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package uk.gov.hmcts.reform.fact.functional.controllers.search;
-
-import tools.jackson.core.type.TypeReference;
-import tools.jackson.databind.ObjectMapper;
-import tools.jackson.databind.cfg.DateTimeFeature;
-import tools.jackson.databind.json.JsonMapper;
-import io.qameta.allure.Feature;
-import io.restassured.response.Response;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-import uk.gov.hmcts.reform.fact.data.api.entities.Court;
-import uk.gov.hmcts.reform.fact.functional.http.HttpClient;
-
-import java.util.List;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.springframework.http.HttpStatus.NOT_FOUND;
-import static org.springframework.http.HttpStatus.OK;
-
-@Feature("Search Service Area Controller")
-@DisplayName("Search Service Area Controller")
-public final class SearchServiceAreaControllerFunctionalTest {
-
- private static final HttpClient http = new HttpClient();
- private static final ObjectMapper mapper = JsonMapper.builder()
- .disable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS)
- .build();
-
- /**
- * This test asserts for an empty list at the moment because there is currently no API endpoint
- * to associate courts with service areas. The only way to create this link is by
- * directly updating the database manually or through a migration script, which is
- * not recommended for functional tests. Once an endpoint to link courts to service
- * areas is available, this test should be updated to create the association and
- * assert that the court is returned.
- */
- @Test
- @DisplayName("GET /search/service-area/v1/{serviceAreaName} returns empty list when no courts are linked")
- void shouldReturnEmptyListForServiceAreaWithNoCourts() {
- final String serviceAreaName = "Tax";
- final Response response = http.doGet("/search/service-area/v1/" + serviceAreaName);
-
- assertThat(response.statusCode())
- .as("Expected 200 OK for valid service area name '%s'", serviceAreaName)
- .isEqualTo(OK.value());
-
- final List courts = mapper.readValue(
- response.getBody().asString(),
- new TypeReference>() {}
- );
-
- assertThat(courts)
- .as("Expected empty list as no courts are currently linked to service area '%s'",
- serviceAreaName)
- .isEmpty();
- }
-
- @Test
- @DisplayName("GET /search/service-area/v1/{serviceAreaName} returns 404 for non-existent service area")
- void shouldReturn404ForNonExistentServiceArea() {
- final String nonExistentServiceArea = "Non Existent Service Area";
- final Response response = http.doGet("/search/service-area/v1/" + nonExistentServiceArea);
-
- assertThat(response.statusCode())
- .as("Expected 404 Not Found for non-existent service area '%s'", nonExistentServiceArea)
- .isEqualTo(NOT_FOUND.value());
- }
-}
diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/helpers/TestDataHelper.java b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/helpers/TestDataHelper.java
index fc71da071..b3814b765 100644
--- a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/helpers/TestDataHelper.java
+++ b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/helpers/TestDataHelper.java
@@ -131,7 +131,6 @@ public static UUID createCourt(final HttpClient http, final String courtName, bo
final Court court = new Court();
court.setName(appendRandomSuffixToCourtName(courtName));
court.setRegionId(UUID.fromString(fetchFirstRegionId(http)));
- court.setIsServiceCentre(true);
court.setOpen(isOpen);
final Response createResponse = http.doPost("/courts/v1", court);
@@ -156,7 +155,6 @@ public static UUID createCourt(
final Court court = new Court();
court.setName(appendRandomSuffixToCourtName(courtName));
court.setRegionId(UUID.fromString(fetchFirstRegionId(http)));
- court.setIsServiceCentre(true);
court.setOpen(isOpen);
court.setMrdId(mrdId);
court.setOpenOnCath(openOnCath);
diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditingTest.java b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditingTest.java
index e6b1ee317..4ea5d0c1e 100644
--- a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditingTest.java
+++ b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditingTest.java
@@ -363,7 +363,6 @@ private Court createCourt(UUID regionId, String name) {
.name(name)
.open(Boolean.FALSE)
.regionId(regionId)
- .isServiceCentre(Boolean.FALSE)
.build();
}
}
diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/AuditControllerTest.java b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/AuditControllerTest.java
index a3feda42b..8bc737ebc 100644
--- a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/AuditControllerTest.java
+++ b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/AuditControllerTest.java
@@ -306,7 +306,6 @@ private Court buildCourt(UUID regionId, String name) {
.name(name)
.open(Boolean.FALSE)
.regionId(regionId)
- .isServiceCentre(Boolean.FALSE)
.build();
}
diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/CourtControllerTest.java b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/CourtControllerTest.java
index 9fcb438a0..6c5cab536 100644
--- a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/CourtControllerTest.java
+++ b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/CourtControllerTest.java
@@ -543,7 +543,6 @@ private Court buildCourt(UUID id) {
.open(Boolean.TRUE)
.warningNotice("Notice")
.regionId(REGION_ID)
- .isServiceCentre(Boolean.TRUE)
.openOnCath(Boolean.TRUE)
.mrdId("MRD123")
.build();
@@ -557,7 +556,6 @@ private CourtDetails buildCourtDetails(UUID id, String name) {
.open(Boolean.TRUE)
.warningNotice("Notice")
.regionId(REGION_ID)
- .isServiceCentre(Boolean.TRUE)
.openOnCath(Boolean.TRUE)
.mrdId("MRD123")
.build();
diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/search/SearchServiceAreaControllerTest.java b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/search/SearchServiceAreaControllerTest.java
deleted file mode 100644
index 11e5ab250..000000000
--- a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/search/SearchServiceAreaControllerTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package uk.gov.hmcts.reform.fact.data.api.controllers.search;
-
-import io.qameta.allure.Feature;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
-import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
-import org.springframework.test.context.bean.override.mockito.MockitoBean;
-import org.springframework.test.web.servlet.MockMvc;
-import uk.gov.hmcts.reform.fact.data.api.entities.CourtServiceAreas;
-import uk.gov.hmcts.reform.fact.data.api.entities.types.CatchmentType;
-import uk.gov.hmcts.reform.fact.data.api.errorhandling.exceptions.NotFoundException;
-import uk.gov.hmcts.reform.fact.data.api.services.CourtServiceAreaService;
-
-import java.util.List;
-import java.util.UUID;
-
-import static org.mockito.Mockito.when;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-
-@Feature("Search Service Area Controller")
-@DisplayName("Search Service Area Controller")
-@WebMvcTest(SearchServiceAreaController.class)
-@AutoConfigureMockMvc(addFilters = false)
-class SearchServiceAreaControllerTest {
-
- private static final UUID COURT_ID = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
- private static final UUID SERVICE_AREA_ID = UUID.fromString("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
-
- @Autowired
- private MockMvc mockMvc;
-
- @MockitoBean
- private CourtServiceAreaService courtServiceAreaService;
-
- @Test
- @DisplayName("GET /search/service-area/v1/{serviceAreaName} returns court service areas")
- void getServiceAreaReturnsOk() throws Exception {
- CourtServiceAreas area = new CourtServiceAreas();
- area.setCourtId(COURT_ID);
- area.setServiceAreaId(List.of(SERVICE_AREA_ID));
- area.setCatchmentType(CatchmentType.REGIONAL);
- area.setCourtName("court name");
- area.setCourtSlug("court-name");
-
- when(courtServiceAreaService.findByServiceAreaName("Family"))
- .thenReturn(List.of(area));
-
- mockMvc.perform(get("/search/service-area/v1/{serviceAreaName}", "Family"))
- .andExpect(status().isOk())
- .andExpect(jsonPath("$[0].courtId").value(COURT_ID.toString()))
- .andExpect(jsonPath("$[0].catchmentType").value("REGIONAL"))
- .andExpect(jsonPath("$[0].courtName").value("court name"))
- .andExpect(jsonPath("$[0].courtSlug").value("court-name"));
- }
-
- @Test
- @DisplayName("GET /search/service-area/v1/{serviceAreaName} returns 404 when missing")
- void getServiceAreaReturnsNotFound() throws Exception {
- when(courtServiceAreaService.findByServiceAreaName("Family"))
- .thenThrow(new NotFoundException("Service area Family not found"));
-
- mockMvc.perform(get("/search/service-area/v1/{serviceAreaName}", "Family"))
- .andExpect(status().isNotFound());
- }
-}
diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/db/CourtLastUpdatedTimeTest.java b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/db/CourtLastUpdatedTimeTest.java
index 84c38fe26..6972d3e8c 100644
--- a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/db/CourtLastUpdatedTimeTest.java
+++ b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/db/CourtLastUpdatedTimeTest.java
@@ -190,7 +190,6 @@ private Court buildCourt(UUID regionId, String name, String slug) {
.open(Boolean.TRUE)
.warningNotice("Notice")
.regionId(regionId)
- .isServiceCentre(Boolean.TRUE)
.openOnCath(Boolean.TRUE)
.mrdId("MRD123")
.build();
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/TestingSupportController.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/TestingSupportController.java
index 87cab59ff..4e605d112 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/TestingSupportController.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/TestingSupportController.java
@@ -83,23 +83,19 @@ public ResponseEntity createSampleCourt(
@RequestParam(required = true) String courtName,
@RequestParam(required = false) UUID regionId,
@RequestParam(required = false) Long seed,
- @RequestParam(required = false, defaultValue = "false") boolean serviceCenter,
@RequestParam(required = false, defaultValue = "true") boolean open,
@RequestParam(required = false, defaultValue = "false") boolean addWarningNotice,
@RequestParam(required = false, defaultValue = "true") boolean withTranslations,
@RequestParam(required = false, defaultValue = "true") boolean withEnquiriesContact,
- @RequestParam(required = false, defaultValue = "false") boolean withServiceAreaAssociation,
@RequestParam(required = false, defaultValue = "false") boolean forceFamilyCourt) {
String courtSlug = testingSupportService.createCourt(
courtName,
regionId,
seed,
- serviceCenter,
open,
addWarningNotice,
withTranslations,
withEnquiriesContact,
- withServiceAreaAssociation,
forceFamilyCourt
);
CourtDetails details = courtService.getCourtDetailsBySlug(courtSlug);
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/search/SearchServiceAreaController.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/search/SearchServiceAreaController.java
deleted file mode 100644
index 651802364..000000000
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/search/SearchServiceAreaController.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package uk.gov.hmcts.reform.fact.data.api.controllers.search;
-
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.responses.ApiResponse;
-import io.swagger.v3.oas.annotations.responses.ApiResponses;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import uk.gov.hmcts.reform.fact.data.api.entities.CourtServiceAreas;
-import uk.gov.hmcts.reform.fact.data.api.security.SecuredFactRestController;
-import uk.gov.hmcts.reform.fact.data.api.services.CourtServiceAreaService;
-
-import java.util.List;
-
-@SecuredFactRestController(
- name = "Search Service Area",
- description = "Operations related to the searching of service areas"
-)
-@RequestMapping("/search/service-area")
-public class SearchServiceAreaController {
-
- private final CourtServiceAreaService courtServiceAreaService;
-
- public SearchServiceAreaController(CourtServiceAreaService courtServiceAreaService) {
- this.courtServiceAreaService = courtServiceAreaService;
- }
-
- @GetMapping("/v1/{serviceAreaName}")
- @Operation(
- summary = "Search for service area by name.",
- description = "Retrieve Service Area based on provided name."
- )
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "Successfully retrieved service area based on provided name."),
- @ApiResponse(responseCode = "400", description = "Service area name is missing or is not valid."),
- @ApiResponse(responseCode = "404", description = "Information not found for provided service area name.")
- })
- public ResponseEntity> getServiceAreaByName(
- @Parameter(description = "Service area name", required = true)
- @PathVariable String serviceAreaName
- ) {
- return ResponseEntity.ok(courtServiceAreaService.findByServiceAreaName(serviceAreaName));
- }
-}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/AbstractCourtEntity.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/AbstractCourtEntity.java
index 4b605af35..77dab9c10 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/AbstractCourtEntity.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/AbstractCourtEntity.java
@@ -87,10 +87,6 @@ public abstract class AbstractCourtEntity {
@Column(name = "region_id")
private UUID regionId;
- @Schema(description = "Indicates that this Court is Service Centre")
- @NotNull
- private Boolean isServiceCentre;
-
@Schema(description = "Indicates that this Court is declared open in the Court and Tribunal Hearing service")
private Boolean openOnCath;
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/CourtServiceAreas.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/CourtServiceAreas.java
deleted file mode 100644
index 547e0859e..000000000
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/CourtServiceAreas.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package uk.gov.hmcts.reform.fact.data.api.entities;
-
-import uk.gov.hmcts.reform.fact.data.api.audit.AuditableCourtEntityListener;
-import uk.gov.hmcts.reform.fact.data.api.entities.types.CatchmentType;
-
-import java.util.List;
-import java.util.UUID;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.persistence.Column;
-import jakarta.persistence.Entity;
-import jakarta.persistence.EntityListeners;
-import jakarta.persistence.EnumType;
-import jakarta.persistence.Enumerated;
-import jakarta.persistence.FetchType;
-import jakarta.persistence.GeneratedValue;
-import jakarta.persistence.GenerationType;
-import jakarta.persistence.Id;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.ManyToOne;
-import jakarta.persistence.Table;
-import jakarta.persistence.Transient;
-import jakarta.validation.constraints.NotNull;
-import lombok.AccessLevel;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import org.hibernate.annotations.JdbcTypeCode;
-import org.hibernate.type.SqlTypes;
-
-@Data
-@AllArgsConstructor(access = AccessLevel.PRIVATE)
-@NoArgsConstructor
-@Builder
-@Entity
-@EntityListeners(AuditableCourtEntityListener.class)
-@Table(name = "court_service_areas", schema = "public")
-public class CourtServiceAreas implements AuditableCourtEntity {
-
- @Schema(
- description = "The internal ID - assigned by the server during creation",
- accessMode = Schema.AccessMode.READ_ONLY
- )
- @Id
- @GeneratedValue(strategy = GenerationType.AUTO)
- private UUID id;
-
- @Schema(description = "The ID of the associated Court", requiredMode = Schema.RequiredMode.REQUIRED)
- @NotNull
- @Column(name = "court_id")
- private UUID courtId;
-
- @JsonIgnore
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "court_id", insertable = false, updatable = false)
- private Court court;
-
- @Schema(description = "The Service Area IDs")
- @JdbcTypeCode(SqlTypes.ARRAY)
- @Column(columnDefinition = "uuid[]")
- private List serviceAreaId;
-
- @Schema(description = "The catchment type")
- @Enumerated(EnumType.STRING)
- private CatchmentType catchmentType;
-
- @Transient
- private String courtName;
-
- @Transient
- private String courtSlug;
-
- // jackson will ignore the above transient fields, so we need to
- // explicitly add them as properties for the API response
-
- @Schema(description = "Has associated court with LOCAL catchment")
- @JsonProperty("courtName")
- public String getCourtName() {
- return courtName;
- }
-
- @Schema(description = "Has associated court with NATIONAL catchment")
- @JsonProperty("courtSlug")
- public String getCourtSlug() {
- return courtSlug;
- }
-
-}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/types/SearchStrategy.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/types/SearchStrategy.java
index 36f1cccfb..df0139b8e 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/types/SearchStrategy.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/types/SearchStrategy.java
@@ -4,12 +4,10 @@
* Enums for search strategies:
* DEFAULT_AOL_DISTANCE is for where individuals are searching for nearest courts.
* FAMILY_NON_REGIONAL family area of law search for one court within a local authority.
- * FAMILY_REGIONAL same as non-regional, but for local-proximity and regional combinations.
* CIVIL_POSTCODE_PREFERENCE civil jurisdictional search based on postcode supplied.
*/
public enum SearchStrategy {
DEFAULT_AOL_DISTANCE,
FAMILY_NON_REGIONAL,
- FAMILY_REGIONAL,
CIVIL_POSTCODE_PREFERENCE
}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/migration/service/CourtMigrationHelper.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/migration/service/CourtMigrationHelper.java
index f47f4f5d7..078b9648e 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/migration/service/CourtMigrationHelper.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/migration/service/CourtMigrationHelper.java
@@ -21,7 +21,6 @@
import uk.gov.hmcts.reform.fact.data.api.entities.CourtFax;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtLocalAuthorities;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtProfessionalInformation;
-import uk.gov.hmcts.reform.fact.data.api.entities.CourtServiceAreas;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtSinglePointsOfEntry;
import uk.gov.hmcts.reform.fact.data.api.entities.types.AllowedLocalAuthorityAreasOfLaw;
import uk.gov.hmcts.reform.fact.data.api.entities.validation.ValidationConstants;
@@ -34,7 +33,6 @@
import uk.gov.hmcts.reform.fact.data.api.migration.model.CourtFaxDto;
import uk.gov.hmcts.reform.fact.data.api.migration.model.CourtLocalAuthorityDto;
import uk.gov.hmcts.reform.fact.data.api.migration.model.CourtProfessionalInformationDto;
-import uk.gov.hmcts.reform.fact.data.api.migration.model.CourtServiceAreaDto;
import uk.gov.hmcts.reform.fact.data.api.migration.model.CourtSinglePointOfEntryDto;
import uk.gov.hmcts.reform.fact.data.api.repositories.AreaOfLawTypeRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtAreasOfLawRepository;
@@ -43,9 +41,7 @@
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtFaxRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtLocalAuthoritiesRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtProfessionalInformationRepository;
-import uk.gov.hmcts.reform.fact.data.api.repositories.CourtServiceAreasRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtSinglePointsOfEntryRepository;
-import uk.gov.hmcts.reform.fact.data.api.repositories.RegionRepository;
import uk.gov.hmcts.reform.fact.data.api.services.CourtService;
import uk.gov.hmcts.reform.fact.data.api.migration.repository.LegacyCourtMappingRepository;
@@ -58,8 +54,6 @@ class CourtMigrationHelper {
private static final Pattern INVALID_GENERIC_DESCRIPTION_CHARACTERS =
Pattern.compile("[^A-Za-z0-9 ()':,-]+");
- private final RegionRepository regionRepository;
- private final CourtServiceAreasRepository courtServiceAreasRepository;
private final CourtAreasOfLawRepository courtAreasOfLawRepository;
private final CourtSinglePointsOfEntryRepository courtSinglePointsOfEntryRepository;
private final CourtLocalAuthoritiesRepository courtLocalAuthoritiesRepository;
@@ -72,8 +66,6 @@ class CourtMigrationHelper {
private final CourtService courtService;
CourtMigrationHelper(
- RegionRepository regionRepository,
- CourtServiceAreasRepository courtServiceAreasRepository,
CourtAreasOfLawRepository courtAreasOfLawRepository,
CourtSinglePointsOfEntryRepository courtSinglePointsOfEntryRepository,
CourtLocalAuthoritiesRepository courtLocalAuthoritiesRepository,
@@ -85,8 +77,6 @@ class CourtMigrationHelper {
LegacyCourtMappingRepository legacyCourtMappingRepository,
CourtService courtService
) {
- this.regionRepository = regionRepository;
- this.courtServiceAreasRepository = courtServiceAreasRepository;
this.courtAreasOfLawRepository = courtAreasOfLawRepository;
this.courtSinglePointsOfEntryRepository = courtSinglePointsOfEntryRepository;
this.courtLocalAuthoritiesRepository = courtLocalAuthoritiesRepository;
@@ -115,6 +105,12 @@ int migrateCourts(List courts, MigrationContext context) {
int total = 0;
for (CourtDto dto : courts) {
+ if (Boolean.TRUE.equals(dto.getIsServiceCentre())) {
+ //TODO: Only migrate courts.
+ LOG.info("Skipping service centre {}", dto.getSlug());
+ continue;
+ }
+
Optional regionId = resolveRegionId(dto, context);
if (regionId.isEmpty()) {
LOG.warn("Skipping court {} because region {} was not migrated", dto.getSlug(), dto.getRegionId());
@@ -140,7 +136,6 @@ int migrateCourts(List courts, MigrationContext context) {
.slug(dto.getSlug())
.open(dto.getOpen())
.regionId(regionId.get())
- .isServiceCentre(dto.getIsServiceCentre())
.build();
Court savedCourt;
@@ -151,7 +146,6 @@ int migrateCourts(List courts, MigrationContext context) {
throw ex;
}
UUID courtId = savedCourt.getId();
- persistCourtServiceAreas(dto.getCourtServiceAreas(), courtId, context);
persistCourtAreasOfLaw(dto.getCourtAreasOfLaw(), courtId, context);
persistCourtSinglePointsOfEntry(dto.getCourtSinglePointsOfEntry(), courtId, context);
persistCourtLocalAuthorities(dto.getCourtLocalAuthorities(), courtId, context);
@@ -165,34 +159,6 @@ int migrateCourts(List courts, MigrationContext context) {
return total;
}
- /**
- * Persists court-service-area joins for a single court.
- *
- * @param serviceAreas legacy service-area relationships.
- * @param courtId identifier of the newly created court.
- * @param context migration context containing service-area ID mappings.
- */
- private void persistCourtServiceAreas(
- List serviceAreas,
- UUID courtId,
- MigrationContext context
- ) {
- if (isEmpty(serviceAreas)) {
- return;
- }
-
- for (CourtServiceAreaDto dto : serviceAreas) {
- CourtServiceAreas.CourtServiceAreasBuilder entityBuilder = CourtServiceAreas.builder()
- .courtId(courtId)
- .serviceAreaId(mapIds(dto.getServiceAreaIds(), context.getServiceAreaIds(), "court service area"))
- ;
- parseCatchmentType(dto.getCatchmentType()).ifPresent(entityBuilder::catchmentType);
- CourtServiceAreas entity = entityBuilder.build();
- courtServiceAreasRepository.save(entity);
- context.courtServiceAreasMigrated++;
- }
- }
-
/**
* Persists the areas-of-law association for the supplied court.
*
@@ -475,8 +441,7 @@ private void persistLegacyCourtMapping(Long legacyCourtId, UUID courtId) {
}
/**
- * Resolves the region ID for the supplied court. Service centres fall back to the dedicated
- * "Service Centre" region when the legacy payload omits a region.
+ * Resolves the region ID for the supplied court.
*
* @param dto legacy court payload.
* @param context migration context containing mapped region IDs.
@@ -487,33 +452,7 @@ private Optional resolveRegionId(CourtDto dto, MigrationContext context) {
if (regionId != null) {
return Optional.of(regionId);
}
- if (!Boolean.TRUE.equals(dto.getIsServiceCentre())) {
- return Optional.empty();
- }
- UUID fallback = context.getServiceCentreRegionId();
- if (fallback == null) {
- Optional loadedFallback = loadServiceCentreRegionId();
- if (loadedFallback.isEmpty()) {
- LOG.warn(
- "Unable to assign region for service centre '{}' because the fallback region was not found",
- dto.getSlug()
- );
- return Optional.empty();
- }
- fallback = loadedFallback.get();
- context.setServiceCentreRegionId(fallback);
- }
- return Optional.of(fallback);
- }
-
- /**
- * Loads the fallback region for service centres.
- *
- * @return identifier of the service-centre region when present.
- */
- private Optional loadServiceCentreRegionId() {
- return regionRepository.findByNameAndCountry("Service Centre", "England")
- .map(uk.gov.hmcts.reform.fact.data.api.entities.Region::getId);
+ return Optional.empty();
}
/**
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/migration/service/MigrationContext.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/migration/service/MigrationContext.java
index a77d24011..4a9fbf597 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/migration/service/MigrationContext.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/migration/service/MigrationContext.java
@@ -12,17 +12,11 @@ class MigrationContext {
private final Map areaOfLawIds = new HashMap<>();
private final Map serviceAreaIds = new HashMap<>();
private final Map> localAuthorityTypeIds = new HashMap<>();
- private UUID serviceCentreRegionId;
int courtAreasOfLawMigrated;
- int courtServiceAreasMigrated;
int courtLocalAuthoritiesMigrated;
int courtSinglePointsOfEntryMigrated;
int courtProfessionalInformationMigrated;
int courtCodesMigrated;
int courtDxCodesMigrated;
int courtFaxMigrated;
-
- void setServiceCentreRegionId(UUID id) {
- this.serviceCentreRegionId = id;
- }
}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/migration/service/MigrationService.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/migration/service/MigrationService.java
index 16d858ccc..a892f3176 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/migration/service/MigrationService.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/migration/service/MigrationService.java
@@ -24,7 +24,6 @@
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtFaxRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtLocalAuthoritiesRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtProfessionalInformationRepository;
-import uk.gov.hmcts.reform.fact.data.api.repositories.CourtServiceAreasRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtSinglePointsOfEntryRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.LocalAuthorityTypeRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.OpeningHoursTypeRepository;
@@ -53,7 +52,6 @@ public MigrationService(
LocalAuthorityTypeRepository localAuthorityTypeRepository,
ContactDescriptionTypeRepository contactDescriptionTypeRepository,
OpeningHoursTypeRepository openingHourTypeRepository,
- CourtServiceAreasRepository courtServiceAreasRepository,
CourtAreasOfLawRepository courtAreasOfLawRepository,
CourtSinglePointsOfEntryRepository courtSinglePointsOfEntryRepository,
CourtLocalAuthoritiesRepository courtLocalAuthoritiesRepository,
@@ -78,8 +76,6 @@ public MigrationService(
openingHourTypeRepository
);
this.courtMigrationHelper = new CourtMigrationHelper(
- regionRepository,
- courtServiceAreasRepository,
courtAreasOfLawRepository,
courtSinglePointsOfEntryRepository,
courtLocalAuthoritiesRepository,
@@ -118,7 +114,7 @@ MigrationSummary persistExport(LegacyExportResponse exportResponse) {
MigrationResult result = new MigrationResult(
courtsMigrated,
context.getCourtAreasOfLawMigrated(),
- context.getCourtServiceAreasMigrated(),
+ 0,
context.getCourtLocalAuthoritiesMigrated(),
context.getCourtSinglePointsOfEntryMigrated(),
context.getCourtProfessionalInformationMigrated(),
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/CourtAddressRepository.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/CourtAddressRepository.java
index 42d4be566..6a9d364dd 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/CourtAddressRepository.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/CourtAddressRepository.java
@@ -235,95 +235,6 @@ List findFamilyNonRegionalByLocalAuthority(
@Param("limit") int limit
);
- /**
- * Finds a regional family court for a local authority and service area.
- *
- * @param serviceAreaId the service area id
- * @param lat the latitude to search from
- * @param lon the longitude to search from
- * @param aolId the area of law id
- * @param localAuthorityId the local authority id
- * @return matching courts with distance data
- */
- @Query(
- value = """
- SELECT DISTINCT ON (c.id)
- c.id as courtId,
- c.name as courtName,
- c.slug as courtSlug,
- (
- point(CAST(ca.lon AS float8), CAST(ca.lat AS float8))
- <@>
- point(CAST(:lon AS float8), CAST(:lat AS float8))
- ) AS distance
- FROM court c
- JOIN court_address ca ON ca.court_id = c.id
- JOIN court_local_authorities cla ON cla.court_id = c.id
- JOIN court_service_areas csa ON csa.court_id = c.id
- WHERE c.open = true
- AND cla.area_of_law_id = CAST(:aolId AS uuid)
- AND CAST(:localAuthorityId AS uuid) = ANY(cla.local_authority_ids)
- AND CAST(:serviceAreaId AS uuid) = ANY(csa.service_area_id)
- AND csa.catchment_type = 'REGIONAL'
- AND ca.address_type IN ('VISIT_US', 'VISIT_OR_CONTACT_US')
- AND ca.lat IS NOT NULL
- AND ca.lon IS NOT NULL
- ORDER BY c.id, distance
- LIMIT 1
- """,
- nativeQuery = true
- )
- List findFamilyRegionalByLocalAuthority(
- @Param("serviceAreaId") UUID serviceAreaId,
- @Param("lat") double lat,
- @Param("lon") double lon,
- @Param("aolId") UUID aolId,
- @Param("localAuthorityId") UUID localAuthorityId
- );
-
- /**
- * Finds a regional family court for a service area and area of law.
- *
- * @param serviceAreaId the service area id
- * @param lat the latitude to search from
- * @param lon the longitude to search from
- * @param aolId the area of law id
- * @return matching courts with distance data
- */
- @Query(
- value = """
- SELECT DISTINCT ON (c.id)
- c.id as courtId,
- c.name as courtName,
- c.slug as courtSlug,
- (
- point(CAST(ca.lon AS float8), CAST(ca.lat AS float8))
- <@>
- point(CAST(:lon AS float8), CAST(:lat AS float8))
- ) AS distance
- FROM court c
- JOIN court_address ca ON ca.court_id = c.id
- JOIN court_service_areas csa ON csa.court_id = c.id
- JOIN court_areas_of_law coa ON coa.court_id = c.id
- WHERE c.open = true
- AND CAST(:serviceAreaId AS uuid) = ANY(csa.service_area_id)
- AND csa.catchment_type = 'REGIONAL'
- AND CAST(:aolId AS uuid) = ANY(coa.areas_of_law)
- AND ca.address_type IN ('VISIT_US', 'VISIT_OR_CONTACT_US')
- AND ca.lat IS NOT NULL
- AND ca.lon IS NOT NULL
- ORDER BY c.id, distance
- LIMIT 1
- """,
- nativeQuery = true
- )
- List findFamilyRegionalByAol(
- @Param("serviceAreaId") UUID serviceAreaId,
- @Param("lat") double lat,
- @Param("lon") double lon,
- @Param("aolId") UUID aolId
- );
-
List findByCourtId(UUID courtId);
Optional findByIdAndCourtId(UUID addressId, UUID courtId);
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/CourtServiceAreasRepository.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/CourtServiceAreasRepository.java
deleted file mode 100644
index b2a49eff9..000000000
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/CourtServiceAreasRepository.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package uk.gov.hmcts.reform.fact.data.api.repositories;
-
-import org.springframework.data.jpa.repository.Query;
-
-import uk.gov.hmcts.reform.fact.data.api.entities.CourtServiceAreas;
-import uk.gov.hmcts.reform.fact.data.api.entities.types.CatchmentType;
-
-import java.util.List;
-import java.util.UUID;
-
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.stereotype.Repository;
-
-@Repository
-public interface CourtServiceAreasRepository extends JpaRepository {
- /**
- * Finds court service area links for a service area id.
- * Note that this query still uses native SQL to match against the Postgres array column with ANY(...).
- *
- * @param id the service area id
- * @return matching court service areas
- */
- @Query(
- value = "SELECT * FROM court_service_areas c WHERE :id = ANY(c.service_area_id)",
- nativeQuery = true
- )
- List findByServiceAreaId(UUID id);
-
- /**
- * checks that a court service area link exists for a set of service area id and catchment types.
- *
- * @param id the service area id
- * @param catchmentTypes the list of catchment types
- * @return true if a matching court service area exists, false otherwise
- */
- @Query(
- value = """
- SELECT EXISTS (
- SELECT
- csa.id
- FROM
- court_service_areas csa
- WHERE
- :id = ANY(csa.service_area_id)
- AND
- csa.catchment_type IN (:#{#catchmentTypes.![name()]})
- LIMIT 1
- )
- """,
- nativeQuery = true
- )
- boolean existsByServiceAreaIdAndCatchmentTypeIn(UUID id, List catchmentTypes);
-}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/CourtService.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/CourtService.java
index f8d92413e..3e69b8afd 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/CourtService.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/CourtService.java
@@ -111,7 +111,7 @@ public Page getFilteredAndPaginatedCourts(int pageNumber, int pageSize, B
}
/**
- * Creates a new court or service centre.
+ * Creates a new court.
*
* @param court The court to create.
* @return The created court.
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/CourtServiceAreaService.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/CourtServiceAreaService.java
deleted file mode 100644
index b2f3c3f77..000000000
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/CourtServiceAreaService.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package uk.gov.hmcts.reform.fact.data.api.services;
-
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Service;
-import uk.gov.hmcts.reform.fact.data.api.entities.CourtServiceAreas;
-import uk.gov.hmcts.reform.fact.data.api.repositories.CourtRepository;
-import uk.gov.hmcts.reform.fact.data.api.repositories.CourtServiceAreasRepository;
-
-import java.util.List;
-import java.util.UUID;
-
-@Service
-@RequiredArgsConstructor
-public class CourtServiceAreaService {
-
- private final CourtServiceAreasRepository courtServiceAreasRepository;
- private final ServiceAreaService serviceAreaService;
- private final CourtRepository courtRepository;
-
- /**
- * Finds court service area links by service area id.
- *
- * @param id the service area id
- * @return matching court service areas
- */
- public List findByServiceAreaId(UUID id) {
- return courtServiceAreasRepository.findByServiceAreaId(id).stream()
- .map(this::enrichCourtServiceAreas)
- .toList();
- }
-
- /**
- * Return the court service area details which includes the court_id on the frontend.
- * We can then call a service centre endpoint from the frontend if the
- * catchment type is NATIONAL.
- *
- * @param serviceAreaName The name of the service area.
- * @return A list of courts that link to the service area.
- */
- public List findByServiceAreaName(String serviceAreaName) {
- return courtServiceAreasRepository.findByServiceAreaId(
- serviceAreaService.getServiceAreaByName(serviceAreaName).getId()).stream()
- .map(this::enrichCourtServiceAreas)
- .toList();
- }
-
- // adds the name and slug for the court into the response
- private CourtServiceAreas enrichCourtServiceAreas(CourtServiceAreas courtServiceAreas) {
- courtRepository.findNameAndSlugById(courtServiceAreas.getCourtId())
- .ifPresent(courtInfo -> {
- courtServiceAreas.setCourtName(courtInfo.name());
- courtServiceAreas.setCourtSlug(courtInfo.slug());
- });
- return courtServiceAreas;
- }
-}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/ServiceAreaService.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/ServiceAreaService.java
index 17092df93..da5d1e1ec 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/ServiceAreaService.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/ServiceAreaService.java
@@ -1,9 +1,7 @@
package uk.gov.hmcts.reform.fact.data.api.services;
import uk.gov.hmcts.reform.fact.data.api.entities.ServiceArea;
-import uk.gov.hmcts.reform.fact.data.api.entities.types.CatchmentType;
import uk.gov.hmcts.reform.fact.data.api.errorhandling.exceptions.NotFoundException;
-import uk.gov.hmcts.reform.fact.data.api.repositories.CourtServiceAreasRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.ServiceAreaRepository;
import java.text.MessageFormat;
@@ -17,7 +15,6 @@
public class ServiceAreaService {
private final ServiceAreaRepository serviceAreaRepository;
- private final CourtServiceAreasRepository courtServiceAreasRepository;
/**
* Retrieves a service area by name.
@@ -59,18 +56,10 @@ public List getAllServiceAreasForService(String serviceName) {
* @return the service area with additional data
*/
private ServiceArea enrichServiceArea(ServiceArea serviceArea) {
- serviceArea.setHasLocal(
- courtServiceAreasRepository.existsByServiceAreaIdAndCatchmentTypeIn(
- serviceArea.getId(), List.of(CatchmentType.LOCAL))
- );
- serviceArea.setHasNational(
- courtServiceAreasRepository.existsByServiceAreaIdAndCatchmentTypeIn(
- serviceArea.getId(), List.of(CatchmentType.NATIONAL))
- );
- serviceArea.setHasRegional(
- courtServiceAreasRepository.existsByServiceAreaIdAndCatchmentTypeIn(
- serviceArea.getId(), List.of(CatchmentType.REGIONAL))
- );
+ //TODO: Re-populate catchment availability from service-centres when that model is introduced.
+ serviceArea.setHasLocal(false);
+ serviceArea.setHasNational(false);
+ serviceArea.setHasRegional(false);
return serviceArea;
}
}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/TestingSupportService.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/TestingSupportService.java
index 754b37d46..14d55acc7 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/TestingSupportService.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/TestingSupportService.java
@@ -17,17 +17,14 @@
import uk.gov.hmcts.reform.fact.data.api.entities.CourtFacilities;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtOpeningHours;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtPhoto;
-import uk.gov.hmcts.reform.fact.data.api.entities.CourtServiceAreas;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtTranslation;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtType;
import uk.gov.hmcts.reform.fact.data.api.entities.LocalAuthorityType;
import uk.gov.hmcts.reform.fact.data.api.entities.OpeningHourType;
import uk.gov.hmcts.reform.fact.data.api.entities.Region;
-import uk.gov.hmcts.reform.fact.data.api.entities.ServiceArea;
import uk.gov.hmcts.reform.fact.data.api.entities.User;
import uk.gov.hmcts.reform.fact.data.api.entities.types.AddressType;
import uk.gov.hmcts.reform.fact.data.api.entities.types.AllowedLocalAuthorityAreasOfLaw;
-import uk.gov.hmcts.reform.fact.data.api.entities.types.CatchmentType;
import uk.gov.hmcts.reform.fact.data.api.entities.types.DayOfTheWeek;
import uk.gov.hmcts.reform.fact.data.api.entities.types.HearingEnhancementEquipment;
import uk.gov.hmcts.reform.fact.data.api.entities.types.OpeningTimesDetail;
@@ -39,12 +36,10 @@
import uk.gov.hmcts.reform.fact.data.api.repositories.AreaOfLawTypeRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.ContactDescriptionTypeRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtPhotoRepository;
-import uk.gov.hmcts.reform.fact.data.api.repositories.CourtServiceAreasRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtTypeRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.LocalAuthorityTypeRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.OpeningHoursTypeRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.RegionRepository;
-import uk.gov.hmcts.reform.fact.data.api.repositories.ServiceAreaRepository;
import java.awt.AlphaComposite;
import java.awt.Color;
@@ -99,7 +94,6 @@ public class TestingSupportService {
private static final List CONTACT_DESCRIPTION_IDS = new ArrayList<>();
private static final List LOCAL_AUTHORITY_TYPE_IDS = new ArrayList<>();
private static final List OPENING_HOUR_TYPE_IDS = new ArrayList<>();
- private static final List SERVICE_AREA_IDS = new ArrayList<>();
// ------------------------------------------------------------------------
// Data variations for randomisation
@@ -182,7 +176,6 @@ public class TestingSupportService {
private final CourtTranslationService courtTranslationService;
private final CourtProfessionalInformationService courtProfessionalInformationService;
private final CourtLocalAuthoritiesService courtLocalAuthoritiesService;
- private final CourtServiceAreasRepository courtServiceAreasRepository;
// for photos, we try the service first, then fall back to the repository if required
private final CourtPhotoService courtPhotoService;
private final CourtPhotoRepository courtPhotoRepository;
@@ -198,7 +191,6 @@ public class TestingSupportService {
private final LocalAuthorityTypeRepository localAuthorityTypeRepository;
private final OpeningHoursTypeRepository openingHoursTypeRepository;
private final RegionRepository regionRepository;
- private final ServiceAreaRepository serviceAreaRepository;
/**
* Creates a court with the specified name and randomised data for all other fields. The randomisation is based on
@@ -206,29 +198,26 @@ public class TestingSupportService {
*
* @param courtName the name of the court to create
* @param seed the seed for randomisation - if not provided, a random seed will be used
- * @param serviceCentre whether the court should be marked as a service centre (true/false)
* @return the slug of the created court entry
**/
public String createCourt(
@NonNull String courtName,
Long seed,
- boolean serviceCentre,
boolean open,
boolean addWarningNotice
) {
- return createCourt(courtName, null, seed, serviceCentre, open, addWarningNotice, true, true, false, false);
+ return createCourt(courtName, null, seed, open, addWarningNotice, true, true, false);
}
public String createCourt(
@NonNull String courtName,
Long seed,
- boolean serviceCentre,
boolean open,
boolean addWarningNotice,
boolean withTranslations
) {
- return createCourt(courtName, null, seed, serviceCentre, open, addWarningNotice, withTranslations, true,
- false, false);
+ return createCourt(courtName, null, seed, open, addWarningNotice, withTranslations, true,
+ false);
}
// Suppressing the "too many params" warning for now as this is a test setup
@@ -238,15 +227,14 @@ public String createCourt(
public String createCourt(
@NonNull String courtName,
Long seed,
- boolean serviceCentre,
boolean open,
boolean addWarningNotice,
boolean withTranslations,
boolean withEnquiriesContact,
- boolean associateServiceAreas
+ boolean forceFamilyCourt
) {
- return createCourt(courtName, null, seed, serviceCentre, open, addWarningNotice, withTranslations,
- withEnquiriesContact, associateServiceAreas, false);
+ return createCourt(courtName, null, seed, open, addWarningNotice, withTranslations,
+ withEnquiriesContact, forceFamilyCourt);
}
// Suppressing the "too many params" warning for now as this is a test setup
@@ -257,12 +245,10 @@ public String createCourt(
@NonNull String courtName,
UUID regionId,
Long seed,
- boolean serviceCentre,
boolean open,
boolean addWarningNotice,
boolean withTranslations,
boolean withEnquiriesContact,
- boolean associateServiceAreas,
boolean forceFamilyCourt
) {
try {
@@ -271,7 +257,7 @@ public String createCourt(
Random random = new Random(Optional.ofNullable(seed).orElse(System.currentTimeMillis()));
- Court court = createCourt(courtName, regionId, serviceCentre, addWarningNotice, random);
+ Court court = createCourt(courtName, regionId, addWarningNotice, random);
UUID courtId = court.getId();
List areasOfLaw = setAreasOfLaw(courtId, random);
List courtTypes = COURT_TYPES.stream().filter(l -> random.nextBoolean()).toList();
@@ -290,9 +276,6 @@ public String createCourt(
setLocalAuthorities(courtId, areasOfLaw, random);
setOpeningHours(courtId, random);
setProfessionalInformation(courtId, forceFamilyCourt, random);
- if (associateServiceAreas) {
- setServiceAreas(courtId, random);
- }
setSinglePointsOfEntry(courtId, areasOfLaw, random);
setTranslations(courtId, random, withTranslations);
setPhotos(courtId, courtName);
@@ -307,12 +290,10 @@ public String createCourt(
private Court createCourt(String name,
UUID regionId,
- boolean serviceCenter,
boolean addWarningNotice,
Random random) {
Court court = Court.builder()
.name(name)
- .isServiceCentre(serviceCenter)
.regionId(Optional.ofNullable(regionId).orElseGet(() -> REGION_IDS.get(random.nextInt(REGION_IDS.size()))))
.mrdId(rndAlphaNumeric(random.nextInt(16, 32), random))
.open(false)
@@ -358,11 +339,9 @@ private void initialiseCaches() {
OPENING_HOUR_TYPE_IDS.addAll(openingHoursTypeRepository.findAll().stream().map(
OpeningHourType::getId).toList()
);
- SERVICE_AREA_IDS.addAll(serviceAreaRepository.findAll().stream().map(ServiceArea::getId).toList());
-
if (REGION_IDS.isEmpty() || AREAS_OF_LAW.isEmpty() || COURT_TYPES.isEmpty()
|| CONTACT_DESCRIPTION_IDS.isEmpty() || LOCAL_AUTHORITY_TYPE_IDS.isEmpty()
- || OPENING_HOUR_TYPE_IDS.isEmpty() || SERVICE_AREA_IDS.isEmpty()) {
+ || OPENING_HOUR_TYPE_IDS.isEmpty()) {
throw new IllegalStateException("Unable to initialize testing support service caches "
+ "- one or more required reference data tables are empty");
}
@@ -681,22 +660,6 @@ private List createFaxNumbers(final Random random) {
return faxNumbers;
}
- private void setServiceAreas(final UUID courtId, final Random random) {
-
- List serviceAreaIds = SERVICE_AREA_IDS.stream().filter(id -> random.nextBoolean()).toList();
- if (serviceAreaIds.isEmpty()) {
- serviceAreaIds = List.of(SERVICE_AREA_IDS.get(random.nextInt(SERVICE_AREA_IDS.size())));
- }
-
- CourtServiceAreas serviceAreas = CourtServiceAreas.builder()
- .courtId(courtId)
- .serviceAreaId(serviceAreaIds)
- .catchmentType(CatchmentType.values()[random.nextInt(CatchmentType.values().length)])
- .build();
-
- courtServiceAreasRepository.save(serviceAreas);
- }
-
private void setSinglePointsOfEntry(final UUID courtId, final List areasOfLaw, final Random random) {
if (random.nextBoolean()) {
List allowedAols = Arrays.stream(AllowedLocalAuthorityAreasOfLaw.values())
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/search/SearchCourtService.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/search/SearchCourtService.java
index 8404c3270..d6fa7b5b1 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/search/SearchCourtService.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/search/SearchCourtService.java
@@ -12,7 +12,6 @@
import uk.gov.hmcts.reform.fact.data.api.os.OsLocationData;
import uk.gov.hmcts.reform.fact.data.api.repositories.LocalAuthorityTypeRepository;
import uk.gov.hmcts.reform.fact.data.api.services.CourtAddressService;
-import uk.gov.hmcts.reform.fact.data.api.services.CourtServiceAreaService;
import uk.gov.hmcts.reform.fact.data.api.services.CourtSinglePointOfEntryService;
import uk.gov.hmcts.reform.fact.data.api.services.OsService;
import uk.gov.hmcts.reform.fact.data.api.services.ServiceAreaService;
@@ -22,12 +21,10 @@
import java.util.Optional;
import static uk.gov.hmcts.reform.fact.data.api.entities.types.CatchmentMethod.LOCAL_AUTHORITY;
-import static uk.gov.hmcts.reform.fact.data.api.entities.types.CatchmentType.REGIONAL;
import static uk.gov.hmcts.reform.fact.data.api.entities.types.SearchAction.NEAREST;
import static uk.gov.hmcts.reform.fact.data.api.entities.types.SearchStrategy.CIVIL_POSTCODE_PREFERENCE;
import static uk.gov.hmcts.reform.fact.data.api.entities.types.SearchStrategy.DEFAULT_AOL_DISTANCE;
import static uk.gov.hmcts.reform.fact.data.api.entities.types.SearchStrategy.FAMILY_NON_REGIONAL;
-import static uk.gov.hmcts.reform.fact.data.api.entities.types.SearchStrategy.FAMILY_REGIONAL;
@Service
@Slf4j
@@ -36,7 +33,6 @@ public class SearchCourtService {
private final OsService osService;
private final ServiceAreaService serviceAreaService;
private final CourtSinglePointOfEntryService courtSinglePointOfEntryService;
- private final CourtServiceAreaService courtServiceAreaService;
private final CourtAddressService courtAddressService;
private final SearchExecuter searchExecuter;
private final LocalAuthorityTypeRepository localAuthorityTypeRepository;
@@ -46,14 +42,12 @@ public class SearchCourtService {
public SearchCourtService(OsService osService,
ServiceAreaService serviceAreaService,
CourtSinglePointOfEntryService courtSinglePointOfEntryService,
- CourtServiceAreaService courtServiceAreaService,
CourtAddressService courtAddressService,
SearchExecuter searchExecuter,
LocalAuthorityTypeRepository localAuthorityTypeRepository) {
this.osService = osService;
this.serviceAreaService = serviceAreaService;
this.courtSinglePointOfEntryService = courtSinglePointOfEntryService;
- this.courtServiceAreaService = courtServiceAreaService;
this.courtAddressService = courtAddressService;
this.searchExecuter = searchExecuter;
this.localAuthorityTypeRepository = localAuthorityTypeRepository;
@@ -182,10 +176,8 @@ public SearchStrategy selectSearchStrategy(SearchAction action,
private SearchStrategy getFamilyStrategy(SearchAction action, ServiceArea serviceArea, String authorityName) {
if (LOCAL_AUTHORITY.equals(serviceArea.getCatchmentMethod())
&& !authorityName.isEmpty()) {
- return courtServiceAreaService.findByServiceAreaId(serviceArea.getId())
- .stream()
- .anyMatch(courtService -> courtService.getCatchmentType().equals(REGIONAL))
- ? FAMILY_REGIONAL : FAMILY_NON_REGIONAL;
+ //TODO: Restore regional service-centre routing when service-centres are reintroduced.
+ return FAMILY_NON_REGIONAL;
}
log.debug("Setting search strategy to default for {}, {}, {}",
action, serviceArea, authorityName);
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/search/SearchExecuter.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/search/SearchExecuter.java
index f4a57ed50..af4a2a16f 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/search/SearchExecuter.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/search/SearchExecuter.java
@@ -55,14 +55,6 @@ public List executeSearchStrategy(OsLocationData osLocationDa
limit,
aolId
);
- case FAMILY_REGIONAL -> executeFamilyRegionalSearchStrategy(
- serviceArea,
- osLocationData,
- lat,
- lon,
- limit,
- aolId
- );
case FAMILY_NON_REGIONAL -> {
List results = executeFamilyNonRegionalSearchStrategy(
osLocationData,
@@ -121,46 +113,6 @@ private List executeCivilSearchStrategy(String postcode, doub
: courtAddressRepository.findNearestByAreaOfLaw(lat, lon, aolId, limit);
}
- /**
- * Execute the family regional search strategy according to the following rules:
- * One of the court service areas need to match a regional service area by ID.
- * The area of law needs to be FAMILY.
- * The catchment also needs to be local-authority.
- *
- * @param serviceArea The service area
- * @param osLocationData The ordnance survey location data
- * @param lat the latitude
- * @param lon the longitude
- * @param limit the limit
- * @param aolId the area of law id
- * @return a list of court with distance objects
- */
- private List executeFamilyRegionalSearchStrategy(ServiceArea serviceArea,
- OsLocationData osLocationData, double lat,
- double lon, int limit, UUID aolId) {
- UUID serviceAreaId = serviceArea.getId();
- return getAuthorityID(osLocationData)
- .map(LocalAuthorityType::getId)
- .map(localAuthorityId -> courtAddressRepository.findFamilyRegionalByLocalAuthority(
- serviceAreaId,
- lat,
- lon,
- aolId,
- localAuthorityId
- ))
- .filter(list -> !list.isEmpty())
- .orElseGet(() -> {
- log.debug("Finding family regional for {}", serviceArea);
- List byAol =
- courtAddressRepository.findFamilyRegionalByAol(serviceAreaId, lat, lon, aolId);
- if (!byAol.isEmpty()) {
- return byAol;
- }
- log.debug("Finding family regional fallback for {}", serviceArea);
- return courtAddressRepository.findNearestByAreaOfLaw(lat, lon, aolId, limit);
- });
- }
-
/**
* Execute the non-regional search strategy.
* This will perform a search that requires the local authority to be present.
diff --git a/src/main/resources/db/migration/V1.27__drop_service_centre_data_structures.sql b/src/main/resources/db/migration/V1.27__drop_service_centre_data_structures.sql
new file mode 100644
index 000000000..73831ba19
--- /dev/null
+++ b/src/main/resources/db/migration/V1.27__drop_service_centre_data_structures.sql
@@ -0,0 +1,9 @@
+ALTER TABLE court DROP COLUMN IF EXISTS is_service_centre;
+
+DROP TRIGGER IF EXISTS court_service_areas_court_update_last_updated_at_trg ON court_service_areas;
+
+DROP TABLE IF EXISTS court_service_areas CASCADE;
+
+DELETE FROM region
+WHERE name = 'Service Centre'
+ AND country = 'England';
diff --git a/src/test/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditableCourtEntityListenerTest.java b/src/test/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditableCourtEntityListenerTest.java
index e5ab44071..51c345156 100644
--- a/src/test/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditableCourtEntityListenerTest.java
+++ b/src/test/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditableCourtEntityListenerTest.java
@@ -147,7 +147,6 @@ private Court createCourt() {
.slug("test-court")
.open(Boolean.FALSE)
.regionId(UUID.randomUUID())
- .isServiceCentre(Boolean.FALSE)
.build();
}
}
diff --git a/src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/CourtControllerTest.java b/src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/CourtControllerTest.java
index 225c38470..801018bb6 100644
--- a/src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/CourtControllerTest.java
+++ b/src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/CourtControllerTest.java
@@ -309,7 +309,6 @@ private static void populateCourt(final AbstractCourtEntity court) {
court.setSlug("test-court");
court.setOpen(Boolean.TRUE);
court.setRegionId(UUID.randomUUID());
- court.setIsServiceCentre(Boolean.TRUE);
court.setOpenOnCath(Boolean.TRUE);
court.setWarningNotice("Warning notice");
court.setMrdId("MRD123");
diff --git a/src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/TestingSupportControllerTest.java b/src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/TestingSupportControllerTest.java
index e8aa95b08..96fe5a554 100644
--- a/src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/TestingSupportControllerTest.java
+++ b/src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/TestingSupportControllerTest.java
@@ -83,17 +83,13 @@ void createSampleCourtPassesTranslationOverride() {
anyBoolean(),
anyBoolean(),
anyBoolean(),
- anyBoolean(),
- anyBoolean(),
anyBoolean()
))
.thenReturn("test-court");
- testingSupportController.createSampleCourt("Test Court", null, 1L, false, true, false, false, false,
- false, false);
+ testingSupportController.createSampleCourt("Test Court", null, 1L, true, false, false, false, false);
- verify(testingSupportService).createCourt("Test Court", null, 1L, false, true, false, false, false,
- false, false);
+ verify(testingSupportService).createCourt("Test Court", null, 1L, true, false, false, false, false);
}
@Test
@@ -107,16 +103,12 @@ void createSampleCourtPassesRegionIdOverride() {
anyBoolean(),
anyBoolean(),
anyBoolean(),
- anyBoolean(),
- anyBoolean(),
anyBoolean()
))
.thenReturn("test-court");
- testingSupportController.createSampleCourt("Test Court", regionId, 1L, false, true, false, false, false,
- false, false);
+ testingSupportController.createSampleCourt("Test Court", regionId, 1L, true, false, false, false, false);
- verify(testingSupportService).createCourt("Test Court", regionId, 1L, false, true, false, false, false,
- false, false);
+ verify(testingSupportService).createCourt("Test Court", regionId, 1L, true, false, false, false, false);
}
}
diff --git a/src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/search/SearchServiceAreaControllerTest.java b/src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/search/SearchServiceAreaControllerTest.java
deleted file mode 100644
index 8b333db0a..000000000
--- a/src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/search/SearchServiceAreaControllerTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package uk.gov.hmcts.reform.fact.data.api.controllers.search;
-
-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.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import uk.gov.hmcts.reform.fact.data.api.entities.CourtServiceAreas;
-import uk.gov.hmcts.reform.fact.data.api.services.CourtServiceAreaService;
-
-import java.util.List;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@ExtendWith(MockitoExtension.class)
-class SearchServiceAreaControllerTest {
-
- @Mock
- private CourtServiceAreaService courtServiceAreaService;
-
- @InjectMocks
- private SearchServiceAreaController controller;
-
- @Test
- void getServiceAreaByNameShouldReturnOk() {
- CourtServiceAreas courtServiceAreas = new CourtServiceAreas();
- List results = List.of(courtServiceAreas);
-
- when(courtServiceAreaService.findByServiceAreaName("Money Claims"))
- .thenReturn(results);
-
- ResponseEntity> response = controller.getServiceAreaByName("Money Claims");
-
- assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
- assertThat(response.getBody()).isEqualTo(results);
- verify(courtServiceAreaService).findByServiceAreaName("Money Claims");
- }
-}
diff --git a/src/test/java/uk/gov/hmcts/reform/fact/data/api/migration/service/CourtMigrationHelperTest.java b/src/test/java/uk/gov/hmcts/reform/fact/data/api/migration/service/CourtMigrationHelperTest.java
index 351f277a5..916764f9b 100644
--- a/src/test/java/uk/gov/hmcts/reform/fact/data/api/migration/service/CourtMigrationHelperTest.java
+++ b/src/test/java/uk/gov/hmcts/reform/fact/data/api/migration/service/CourtMigrationHelperTest.java
@@ -7,7 +7,6 @@
import static org.mockito.Mockito.when;
import java.util.List;
-import java.util.Optional;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -21,7 +20,6 @@
import uk.gov.hmcts.reform.fact.data.api.entities.CourtDxCode;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtLocalAuthorities;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtProfessionalInformation;
-import uk.gov.hmcts.reform.fact.data.api.entities.Region;
import uk.gov.hmcts.reform.fact.data.api.entities.types.AllowedLocalAuthorityAreasOfLaw;
import uk.gov.hmcts.reform.fact.data.api.migration.model.CourtAreasOfLawDto;
import uk.gov.hmcts.reform.fact.data.api.migration.model.CourtDto;
@@ -37,17 +35,13 @@
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtFaxRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtLocalAuthoritiesRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtProfessionalInformationRepository;
-import uk.gov.hmcts.reform.fact.data.api.repositories.CourtServiceAreasRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtSinglePointsOfEntryRepository;
-import uk.gov.hmcts.reform.fact.data.api.repositories.RegionRepository;
import uk.gov.hmcts.reform.fact.data.api.services.CourtService;
import uk.gov.hmcts.reform.fact.data.api.migration.repository.LegacyCourtMappingRepository;
@ExtendWith(MockitoExtension.class)
class CourtMigrationHelperTest {
- @Mock private RegionRepository regionRepository;
- @Mock private CourtServiceAreasRepository courtServiceAreasRepository;
@Mock private CourtAreasOfLawRepository courtAreasOfLawRepository;
@Mock private CourtSinglePointsOfEntryRepository courtSinglePointsOfEntryRepository;
@Mock private CourtLocalAuthoritiesRepository courtLocalAuthoritiesRepository;
@@ -65,8 +59,6 @@ class CourtMigrationHelperTest {
@BeforeEach
void setUp() {
helper = new CourtMigrationHelper(
- regionRepository,
- courtServiceAreasRepository,
courtAreasOfLawRepository,
courtSinglePointsOfEntryRepository,
courtLocalAuthoritiesRepository,
@@ -98,7 +90,6 @@ void shouldPersistCourtRelationshipsAndIncrementCounters() {
context.getRegionIds().put(1, regionId);
context.getAreaOfLawIds().put(10, childrenAolID);
- context.getServiceAreaIds().put(100, UUID.randomUUID());
context.getLocalAuthorityTypeIds().put(20, List.of(UUID.randomUUID()));
when(courtService.createCourt(any(Court.class))).thenAnswer(invocation -> {
Court court = invocation.getArgument(0);
@@ -120,23 +111,13 @@ void shouldPersistCourtRelationshipsAndIncrementCounters() {
int migrated = helper.migrateCourts(List.of(courtDto), context);
assertThat(migrated).isEqualTo(1);
- assertThat(context.getCourtServiceAreasMigrated()).isEqualTo(1);
assertThat(context.getCourtAreasOfLawMigrated()).isEqualTo(1);
assertThat(context.getCourtLocalAuthoritiesMigrated()).isEqualTo(1);
assertThat(context.getCourtSinglePointsOfEntryMigrated()).isEqualTo(1);
}
@Test
- void shouldUseServiceCentreFallbackRegion() {
- UUID fallbackRegion = UUID.randomUUID();
- when(regionRepository.findByNameAndCountry("Service Centre", "England"))
- .thenReturn(Optional.of(Region.builder().id(fallbackRegion).build()));
- when(courtService.createCourt(any(Court.class))).thenAnswer(invocation -> {
- Court court = invocation.getArgument(0);
- court.setId(UUID.randomUUID());
- return court;
- });
-
+ void shouldSkipServiceCentre() {
CourtDto serviceCentre = new CourtDto();
serviceCentre.setId(600L);
serviceCentre.setName("Service Centre Court");
@@ -145,8 +126,8 @@ void shouldUseServiceCentreFallbackRegion() {
serviceCentre.setIsServiceCentre(true);
int migrated = helper.migrateCourts(List.of(serviceCentre), context);
- assertThat(migrated).isEqualTo(1);
- assertThat(context.getServiceCentreRegionId()).isEqualTo(fallbackRegion);
+ assertThat(migrated).isZero();
+ verify(courtService, never()).createCourt(any(Court.class));
}
@Test
diff --git a/src/test/java/uk/gov/hmcts/reform/fact/data/api/migration/service/MigrationServiceTest.java b/src/test/java/uk/gov/hmcts/reform/fact/data/api/migration/service/MigrationServiceTest.java
index 0c134438d..46e33ce3e 100644
--- a/src/test/java/uk/gov/hmcts/reform/fact/data/api/migration/service/MigrationServiceTest.java
+++ b/src/test/java/uk/gov/hmcts/reform/fact/data/api/migration/service/MigrationServiceTest.java
@@ -60,7 +60,6 @@
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtLocalAuthoritiesRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtProfessionalInformationRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtRepository;
-import uk.gov.hmcts.reform.fact.data.api.repositories.CourtServiceAreasRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtSinglePointsOfEntryRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtTypeRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.LocalAuthorityTypeRepository;
@@ -110,8 +109,6 @@ class MigrationServiceTest {
@Mock
private CourtRepository courtRepository;
@Mock
- private CourtServiceAreasRepository courtServiceAreasRepository;
- @Mock
private CourtAreasOfLawRepository courtAreasOfLawRepository;
@Mock
private CourtSinglePointsOfEntryRepository courtSinglePointsOfEntryRepository;
@@ -261,7 +258,7 @@ void shouldPersistLegacyDataAndReturnSummary() {
assertThat(result.getCourtsMigrated()).isEqualTo(1);
assertThat(result.getCourtAreasOfLawMigrated()).isEqualTo(1);
- assertThat(result.getCourtServiceAreasMigrated()).isEqualTo(1);
+ assertThat(result.getCourtServiceAreasMigrated()).isZero();
assertThat(result.getCourtLocalAuthoritiesMigrated()).isEqualTo(1);
assertThat(result.getCourtSinglePointsOfEntryMigrated()).isEqualTo(1);
assertThat(result.getCourtProfessionalInformationMigrated()).isEqualTo(1);
@@ -270,7 +267,6 @@ void shouldPersistLegacyDataAndReturnSummary() {
assertThat(result.getCourtFaxMigrated()).isEqualTo(1);
verify(legacyServiceRepository).save(any(LegacyService.class));
- verify(courtServiceAreasRepository).save(any());
verify(courtAreasOfLawRepository).save(any());
verify(courtSinglePointsOfEntryRepository).save(any());
verify(courtLocalAuthoritiesRepository).save(any());
@@ -310,7 +306,7 @@ void shouldSkipCourtDxCodesWhenDetailsMissing() {
}
@Test
- void shouldAssignFallbackRegionToServiceCentresWithoutRegion() {
+ void shouldSkipServiceCentres() {
CourtDto serviceCentre = new CourtDto(
999L,
"Service Centre Court",
@@ -347,11 +343,8 @@ void shouldAssignFallbackRegionToServiceCentresWithoutRegion() {
MigrationSummary summary = migrationService.migrate();
- assertThat(summary.getResult().getCourtsMigrated()).isEqualTo(1);
- ArgumentCaptor captor = ArgumentCaptor.forClass(Court.class);
- verify(courtService).createCourt(captor.capture());
- assertThat(captor.getValue().getRegionId()).isNotNull();
- verify(regionRepository).findByNameAndCountry("Service Centre", "England");
+ assertThat(summary.getResult().getCourtsMigrated()).isZero();
+ verify(courtService, never()).createCourt(any(Court.class));
}
private CourtDto courtDto() {
@@ -370,7 +363,7 @@ private CourtDto courtDto() {
List.of(new CourtDxCodeDto("dx", "123", "dx explanation")),
List.of(new CourtFaxDto("fax", "01632960000")),
null,
- true
+ false
);
}
@@ -419,7 +412,7 @@ private CourtDto courtDtoWithEmptyDxCode() {
List.of(new CourtDxCodeDto("dx", null, null)),
List.of(new CourtFaxDto("fax", "01632960000")),
null,
- true
+ false
);
}
diff --git a/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/CourtServiceAreaServiceTest.java b/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/CourtServiceAreaServiceTest.java
deleted file mode 100644
index b6b340f00..000000000
--- a/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/CourtServiceAreaServiceTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-package uk.gov.hmcts.reform.fact.data.api.services;
-
-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 uk.gov.hmcts.reform.fact.data.api.entities.CourtServiceAreas;
-import uk.gov.hmcts.reform.fact.data.api.entities.ServiceArea;
-import uk.gov.hmcts.reform.fact.data.api.repositories.CourtRepository;
-import uk.gov.hmcts.reform.fact.data.api.repositories.CourtServiceAreasRepository;
-
-import java.util.List;
-import java.util.UUID;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@ExtendWith(MockitoExtension.class)
-class CourtServiceAreaServiceTest {
-
- @Mock
- private CourtServiceAreasRepository courtServiceAreasRepository;
-
- @Mock
- private CourtRepository courtRepository;
-
- @Mock
- private ServiceAreaService serviceAreaService;
-
- @InjectMocks
- private CourtServiceAreaService courtServiceAreaService;
-
- @Test
- void findByServiceAreaIdShouldReturnResults() {
- UUID serviceAreaId = UUID.randomUUID();
- List results = List.of(new CourtServiceAreas());
- when(courtServiceAreasRepository.findByServiceAreaId(serviceAreaId)).thenReturn(results);
-
- List response = courtServiceAreaService.findByServiceAreaId(serviceAreaId);
-
- assertThat(response).isEqualTo(results);
- verify(courtServiceAreasRepository).findByServiceAreaId(serviceAreaId);
- }
-
- @Test
- void findByServiceAreaNameShouldResolveServiceAreaAndReturnResults() {
- UUID serviceAreaId = UUID.randomUUID();
- ServiceArea area = new ServiceArea();
- area.setId(serviceAreaId);
- List results = List.of(new CourtServiceAreas());
-
- when(serviceAreaService.getServiceAreaByName("Money Claims")).thenReturn(area);
- when(courtServiceAreasRepository.findByServiceAreaId(serviceAreaId)).thenReturn(results);
-
- List response = courtServiceAreaService.findByServiceAreaName("Money Claims");
-
- assertThat(response).isEqualTo(results);
- verify(serviceAreaService).getServiceAreaByName("Money Claims");
- verify(courtServiceAreasRepository).findByServiceAreaId(serviceAreaId);
- }
-
- @Test
- void findByServiceAreaIdShouldEnrichCourtServiceAreasWithNameAndSlug() {
- UUID serviceAreaId = UUID.randomUUID();
- UUID courtId = UUID.randomUUID();
- CourtServiceAreas csa = new CourtServiceAreas();
- csa.setCourtId(courtId);
- List results = List.of(csa);
- when(courtServiceAreasRepository.findByServiceAreaId(serviceAreaId)).thenReturn(results);
- when(courtRepository.findNameAndSlugById(courtId))
- .thenReturn(java.util.Optional.of(new CourtRepository.NameAndSlug("Court Name", "court-slug")));
-
- List response = courtServiceAreaService.findByServiceAreaId(serviceAreaId);
-
- assertThat(response.getFirst().getCourtName()).isEqualTo("Court Name");
- assertThat(response.getFirst().getCourtSlug()).isEqualTo("court-slug");
- verify(courtRepository).findNameAndSlugById(courtId);
- }
-
- @Test
- void findByServiceAreaNameShouldEnrichCourtServiceAreasWithNameAndSlug() {
- UUID serviceAreaId = UUID.randomUUID();
- UUID courtId = UUID.randomUUID();
- ServiceArea area = new ServiceArea();
- area.setId(serviceAreaId);
- CourtServiceAreas csa = new CourtServiceAreas();
- csa.setCourtId(courtId);
- List results = List.of(csa);
- when(serviceAreaService.getServiceAreaByName("Money Claims")).thenReturn(area);
- when(courtServiceAreasRepository.findByServiceAreaId(serviceAreaId)).thenReturn(results);
- when(courtRepository.findNameAndSlugById(courtId))
- .thenReturn(java.util.Optional.of(new CourtRepository.NameAndSlug("Court Name", "court-slug")));
-
- List response = courtServiceAreaService.findByServiceAreaName("Money Claims");
-
- assertThat(response.getFirst().getCourtName()).isEqualTo("Court Name");
- assertThat(response.getFirst().getCourtSlug()).isEqualTo("court-slug");
- verify(courtRepository).findNameAndSlugById(courtId);
- }
-
- @Test
- void enrichmentShouldHandleMissingCourtNameAndSlug() {
- UUID serviceAreaId = UUID.randomUUID();
- UUID courtId = UUID.randomUUID();
- CourtServiceAreas csa = new CourtServiceAreas();
- csa.setCourtId(courtId);
- List results = List.of(csa);
- when(courtServiceAreasRepository.findByServiceAreaId(serviceAreaId)).thenReturn(results);
- when(courtRepository.findNameAndSlugById(courtId)).thenReturn(java.util.Optional.empty());
-
- List response = courtServiceAreaService.findByServiceAreaId(serviceAreaId);
-
- assertThat(response.getFirst().getCourtName()).isNull();
- assertThat(response.getFirst().getCourtSlug()).isNull();
- verify(courtRepository).findNameAndSlugById(courtId);
- }
-}
diff --git a/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/SearchCourtServiceTest.java b/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/SearchCourtServiceTest.java
index 3bdc85eef..0a0ccd5be 100644
--- a/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/SearchCourtServiceTest.java
+++ b/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/SearchCourtServiceTest.java
@@ -8,11 +8,9 @@
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import uk.gov.hmcts.reform.fact.data.api.dto.CourtWithDistance;
-import uk.gov.hmcts.reform.fact.data.api.entities.CourtServiceAreas;
import uk.gov.hmcts.reform.fact.data.api.entities.LocalAuthorityType;
import uk.gov.hmcts.reform.fact.data.api.entities.ServiceArea;
import uk.gov.hmcts.reform.fact.data.api.entities.types.CatchmentMethod;
-import uk.gov.hmcts.reform.fact.data.api.entities.types.CatchmentType;
import uk.gov.hmcts.reform.fact.data.api.entities.types.SearchAction;
import uk.gov.hmcts.reform.fact.data.api.entities.types.SearchStrategy;
import uk.gov.hmcts.reform.fact.data.api.entities.types.ServiceAreaType;
@@ -55,9 +53,6 @@ class SearchCourtServiceTest {
@Mock
private CourtSinglePointOfEntryService courtSinglePointOfEntryService;
- @Mock
- private CourtServiceAreaService courtServiceAreaService;
-
@Mock
private CourtAddressService courtAddressService;
@@ -177,33 +172,9 @@ void selectSearchStrategyShouldReturnCivilPreference() {
}
@Test
- void selectSearchStrategyShouldReturnFamilyRegionalWhenRegionalCatchmentExists() {
- ServiceArea area = serviceAreaWithType(ServiceAreaType.FAMILY);
- area.setCatchmentMethod(CatchmentMethod.LOCAL_AUTHORITY);
- CourtServiceAreas courtServiceAreas = new CourtServiceAreas();
- courtServiceAreas.setCatchmentType(CatchmentType.REGIONAL);
-
- when(courtServiceAreaService.findByServiceAreaId(area.getId()))
- .thenReturn(List.of(courtServiceAreas));
-
- SearchStrategy strategy = searchCourtService.selectSearchStrategy(
- SearchAction.DOCUMENTS,
- "Authority",
- area
- );
-
- assertThat(strategy).isEqualTo(SearchStrategy.FAMILY_REGIONAL);
- }
-
- @Test
- void selectSearchStrategyShouldReturnFamilyNonRegionalWhenNoRegionalCatchment() {
+ void selectSearchStrategyShouldReturnFamilyNonRegionalForLocalAuthorityCatchment() {
ServiceArea area = serviceAreaWithType(ServiceAreaType.FAMILY);
area.setCatchmentMethod(CatchmentMethod.LOCAL_AUTHORITY);
- CourtServiceAreas courtServiceAreas = new CourtServiceAreas();
- courtServiceAreas.setCatchmentType(CatchmentType.LOCAL);
-
- when(courtServiceAreaService.findByServiceAreaId(area.getId()))
- .thenReturn(List.of(courtServiceAreas));
SearchStrategy strategy = searchCourtService.selectSearchStrategy(
SearchAction.DOCUMENTS,
diff --git a/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/ServiceAreaServiceTest.java b/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/ServiceAreaServiceTest.java
index 1835b106e..8f5c7679f 100644
--- a/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/ServiceAreaServiceTest.java
+++ b/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/ServiceAreaServiceTest.java
@@ -7,7 +7,6 @@
import org.mockito.junit.jupiter.MockitoExtension;
import uk.gov.hmcts.reform.fact.data.api.entities.ServiceArea;
import uk.gov.hmcts.reform.fact.data.api.errorhandling.exceptions.NotFoundException;
-import uk.gov.hmcts.reform.fact.data.api.repositories.CourtServiceAreasRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.ServiceAreaRepository;
import java.util.List;
@@ -24,9 +23,6 @@ class ServiceAreaServiceTest {
@Mock
private ServiceAreaRepository serviceAreaRepository;
- @Mock
- private CourtServiceAreasRepository courtServiceAreasRepository;
-
@InjectMocks
private ServiceAreaService serviceAreaService;
diff --git a/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/TestingSupportServiceTest.java b/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/TestingSupportServiceTest.java
index 957b264d1..eef35fe82 100644
--- a/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/TestingSupportServiceTest.java
+++ b/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/TestingSupportServiceTest.java
@@ -27,13 +27,11 @@
import uk.gov.hmcts.reform.fact.data.api.entities.CourtFacilities;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtOpeningHours;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtPhoto;
-import uk.gov.hmcts.reform.fact.data.api.entities.CourtServiceAreas;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtTranslation;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtType;
import uk.gov.hmcts.reform.fact.data.api.entities.LocalAuthorityType;
import uk.gov.hmcts.reform.fact.data.api.entities.OpeningHourType;
import uk.gov.hmcts.reform.fact.data.api.entities.Region;
-import uk.gov.hmcts.reform.fact.data.api.entities.ServiceArea;
import uk.gov.hmcts.reform.fact.data.api.entities.User;
import uk.gov.hmcts.reform.fact.data.api.entities.types.AllowedLocalAuthorityAreasOfLaw;
import uk.gov.hmcts.reform.fact.data.api.models.AreaOfLawSelectionDto;
@@ -41,12 +39,10 @@
import uk.gov.hmcts.reform.fact.data.api.repositories.AreaOfLawTypeRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.ContactDescriptionTypeRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtPhotoRepository;
-import uk.gov.hmcts.reform.fact.data.api.repositories.CourtServiceAreasRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtTypeRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.LocalAuthorityTypeRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.OpeningHoursTypeRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.RegionRepository;
-import uk.gov.hmcts.reform.fact.data.api.repositories.ServiceAreaRepository;
import java.util.List;
import java.util.Optional;
@@ -89,8 +85,6 @@ class TestingSupportServiceTest {
@Mock
private CourtLocalAuthoritiesService courtLocalAuthoritiesService;
@Mock
- private CourtServiceAreasRepository courtServiceAreasRepository;
- @Mock
private CourtPhotoService courtPhotoService;
@Mock
private CourtPhotoRepository courtPhotoRepository;
@@ -110,9 +104,6 @@ class TestingSupportServiceTest {
private OpeningHoursTypeRepository openingHoursTypeRepository;
@Mock
private RegionRepository regionRepository;
- @Mock
- private ServiceAreaRepository serviceAreaRepository;
-
@InjectMocks
private TestingSupportService testingSupportService;
@@ -121,12 +112,10 @@ class TestingSupportServiceTest {
private final List areasOfLaw = randomAreasOfLaw();
private final UUID auditUserId = UUID.randomUUID();
-
private final List courtTypes = randomCourtTypes();
private final List contactDescIds = randomUuidList();
private final List localAuthorityIds = randomUuidList();
private final List openingHourTypeIds = randomUuidList();
- private final List serviceAreaIds = randomUuidList();
private static List randomAreasOfLaw() {
Random random = new Random();
@@ -202,10 +191,6 @@ void setup() {
lenient().when(openingHoursTypeRepository.findAll()).thenReturn(
openingHourTypeIds.stream().map(id -> OpeningHourType.builder().id(id).build()).toList()
);
- // ServiceAreaRepository
- lenient().when(serviceAreaRepository.findAll()).thenReturn(
- serviceAreaIds.stream().map(id -> ServiceArea.builder().id(id).build()).toList()
- );
lenient().when(auditUserContext.getUserId()).thenReturn(Optional.of(auditUserId));
lenient().when(auditUserContext.requireUserId()).thenReturn(auditUserId);
lenient().when(userService.createOrUpdateLastLoginUser(any(User.class))).thenAnswer(inv -> {
@@ -221,10 +206,11 @@ void createCourtWithValidName() {
when(courtService.createCourt(any())).thenAnswer(inv -> {
Court c = Court.class.cast(inv.getArguments()[0]);
c.setSlug("test-court");
+ c.setId(UUID.randomUUID());
return c;
});
- String result = testingSupportService.createCourt(courtName, null, false, false, true);
+ String result = testingSupportService.createCourt(courtName, null, false, true);
assertNotNull(result);
assertEquals("test-court", result);
@@ -244,7 +230,6 @@ void createCourtWithValidName() {
verify(courtAddressService, atLeast(1)).createAddress(any(), any());
// things that are optional, but have a ceiling
- verify(courtServiceAreasRepository, atMost(1)).save(any());
verify(courtLocalAuthoritiesService, atMost(1)).setCourtLocalAuthorities(any(), any());
verify(courtTranslationService, times(1)).setTranslation(any(), any());
verify(courtPhotoRepository, atMost(1)).save(any());
@@ -258,10 +243,11 @@ void createCourtWithWarningNotice() {
when(courtService.createCourt(any())).thenAnswer(inv -> {
Court c = Court.class.cast(inv.getArguments()[0]);
c.setSlug("test-court");
+ c.setId(UUID.randomUUID());
return c;
});
- String result = testingSupportService.createCourt(courtName, null, false, false, true);
+ String result = testingSupportService.createCourt(courtName, null, false, true);
assertNotNull(result);
assertEquals("test-court", result);
@@ -277,10 +263,11 @@ void createCourtWithoutWarningNotice() {
when(courtService.createCourt(any())).thenAnswer(inv -> {
Court c = Court.class.cast(inv.getArguments()[0]);
c.setSlug("test-court");
+ c.setId(UUID.randomUUID());
return c;
});
- String result = testingSupportService.createCourt(courtName, null, false, false, false);
+ String result = testingSupportService.createCourt(courtName, null, false, false);
assertNotNull(result);
assertEquals("test-court", result);
@@ -297,11 +284,12 @@ void createCourtWithRegionIdUsesProvidedRegion() {
when(courtService.createCourt(any())).thenAnswer(inv -> {
Court c = Court.class.cast(inv.getArguments()[0]);
c.setSlug("test-court");
+ c.setId(UUID.randomUUID());
return c;
});
- String result = testingSupportService.createCourt(courtName, regionId, null, false, false, true,
- true, false, false, false);
+ String result = testingSupportService.createCourt(courtName, regionId, null, false, true,
+ true, false, false);
assertNotNull(result);
assertEquals("test-court", result);
@@ -316,7 +304,7 @@ void createCourtWithEmptyNameThrowsException() {
String courtName = "";
assertThrows(
NullPointerException.class,
- () -> testingSupportService.createCourt(courtName, null, false, false, true)
+ () -> testingSupportService.createCourt(courtName, null, false, true)
);
}
@@ -326,10 +314,11 @@ void createCourtWithoutTranslationsSkipsTranslationService() {
when(courtService.createCourt(any())).thenAnswer(inv -> {
Court c = Court.class.cast(inv.getArguments()[0]);
c.setSlug("test-court");
+ c.setId(UUID.randomUUID());
return c;
});
- String result = testingSupportService.createCourt(courtName, null, false, false, true, false);
+ String result = testingSupportService.createCourt(courtName, null, false, true, false);
assertNotNull(result);
assertEquals("test-court", result);
@@ -342,49 +331,17 @@ void createCourtWithoutEnquiriesContactSkipsContactCreation() {
when(courtService.createCourt(any())).thenAnswer(inv -> {
Court c = Court.class.cast(inv.getArguments()[0]);
c.setSlug("test-court");
+ c.setId(UUID.randomUUID());
return c;
});
- String result = testingSupportService.createCourt(courtName, null, false, false, true, true, false, false);
+ String result = testingSupportService.createCourt(courtName, null, false, true, true, false, false);
assertNotNull(result);
assertEquals("test-court", result);
verify(courtContactDetailsService, never()).createContactDetail(any(), any());
}
- @Test
- void createCourtWithoutAssociateServiceAreasSkipsServiceAreaAssociation() {
- String courtName = "Test Court";
- when(courtService.createCourt(any())).thenAnswer(inv -> {
- Court c = Court.class.cast(inv.getArguments()[0]);
- c.setSlug("test-court");
- return c;
- });
-
- String result = testingSupportService.createCourt(courtName, null, false, false, true, true, false, false);
-
- assertNotNull(result);
- assertEquals("test-court", result);
- verify(courtServiceAreasRepository, never()).save(any());
- }
-
- @Test
- void createCourtWithAssociateServiceAreasPerformsServiceAreaAssociation() {
- String courtName = "Test Court";
- when(courtService.createCourt(any())).thenAnswer(inv -> {
- Court c = Court.class.cast(inv.getArguments()[0]);
- c.setSlug("test-court");
- return c;
- });
-
- String result = testingSupportService.createCourt(courtName, null, false, false, true, true, false, true);
-
- assertNotNull(result);
- assertEquals("test-court", result);
- verify(courtServiceAreasRepository, times(1)).save(any());
- }
-
-
@Captor
private ArgumentCaptor> aolSelectionDtoArgumentCaptor;
@Captor
@@ -404,7 +361,7 @@ void createCourtWithSeedGeneratesConsistentResults() {
return c;
});
- testingSupportService.createCourt(courtName, seed, false, false, true);
+ testingSupportService.createCourt(courtName, seed, false, true);
// things that are always called once
verify(courtService, times(1)).createCourt(any());
@@ -455,13 +412,8 @@ void createCourtWithSeedGeneratesConsistentResults() {
verify(courtPhotoRepository, atMost(1)).save(courtPhotoArgumentCaptor.capture());
verify(courtSinglePointsOfEntryService, atMost(1))
.updateCourtSinglePointsOfEntry(eq(courtId), aolSelectionDtoArgumentCaptor.capture());
- ArgumentCaptor courtServiceAreasArgumentCaptor =
- ArgumentCaptor.forClass(CourtServiceAreas.class);
- verify(courtServiceAreasRepository, atMost(1))
- .save(courtServiceAreasArgumentCaptor.capture());
-
// second call with same seed should generate same results
- testingSupportService.createCourt(courtName, seed, false, false, true);
+ testingSupportService.createCourt(courtName, seed, false, true);
verify(courtService, times(2)).createCourt(any());
@@ -513,9 +465,5 @@ void createCourtWithSeedGeneratesConsistentResults() {
times(2)
).updateCourtSinglePointsOfEntry(courtId, v)
);
- courtServiceAreasArgumentCaptor.getAllValues().forEach(v -> verify(
- courtServiceAreasRepository,
- times(2)
- ).save(v));
}
}
diff --git a/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/search/SearchExecuterTest.java b/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/search/SearchExecuterTest.java
index 63875eb04..bd21c2487 100644
--- a/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/search/SearchExecuterTest.java
+++ b/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/search/SearchExecuterTest.java
@@ -121,98 +121,6 @@ void executeSearchStrategyShouldFallbackToNearestWhenCivilTieredResultsEmpty() {
verify(courtAddressRepository).findNearestByAreaOfLaw(51.5, -0.1, area.getAreaOfLawId(), 5);
}
- @Test
- void executeSearchStrategyShouldReturnFamilyRegionalByLocalAuthorityWhenFound() {
- ServiceArea area = serviceArea(ServiceAreaType.FAMILY);
- OsLocationData locationData = osLocationData("Authority", "SW1A 1AA");
- LocalAuthorityType authorityType = localAuthorityType(UUID.randomUUID());
- List results = List.of(mock(CourtWithDistance.class));
-
- when(localAuthorityTypeRepository.findIdByNameIgnoreCase("Authority"))
- .thenReturn(Optional.of(authorityType));
- when(courtAddressRepository.findFamilyRegionalByLocalAuthority(
- area.getId(),
- 51.5,
- -0.1,
- area.getAreaOfLawId(),
- authorityType.getId()
- )).thenReturn(results);
-
- List response = searchExecuter.executeSearchStrategy(
- locationData,
- area,
- SearchStrategy.FAMILY_REGIONAL,
- SearchAction.DOCUMENTS,
- 10
- );
-
- assertThat(response).isEqualTo(results);
- verify(courtAddressRepository, never()).findFamilyRegionalByAol(any(), anyDouble(), anyDouble(), any());
- }
-
- @Test
- void executeSearchStrategyShouldFallbackToRegionalByAolWhenLocalAuthorityEmpty() {
- ServiceArea area = serviceArea(ServiceAreaType.FAMILY);
- OsLocationData locationData = osLocationData("Authority", "SW1A 1AA");
- LocalAuthorityType authorityType = localAuthorityType(UUID.randomUUID());
- List results = List.of(mock(CourtWithDistance.class));
-
- when(localAuthorityTypeRepository.findIdByNameIgnoreCase("Authority"))
- .thenReturn(Optional.of(authorityType));
- when(courtAddressRepository.findFamilyRegionalByLocalAuthority(
- area.getId(),
- 51.5,
- -0.1,
- area.getAreaOfLawId(),
- authorityType.getId()
- )).thenReturn(List.of());
- when(courtAddressRepository.findFamilyRegionalByAol(area.getId(), 51.5, -0.1, area.getAreaOfLawId()))
- .thenReturn(results);
-
- List response = searchExecuter.executeSearchStrategy(
- locationData,
- area,
- SearchStrategy.FAMILY_REGIONAL,
- SearchAction.DOCUMENTS,
- 10
- );
-
- assertThat(response).isEqualTo(results);
- verify(courtAddressRepository, never()).findNearestByAreaOfLaw(anyDouble(), anyDouble(), any(), anyInt());
- }
-
- @Test
- void executeSearchStrategyShouldFallbackToNearestWhenRegionalResultsMissing() {
- ServiceArea area = serviceArea(ServiceAreaType.FAMILY);
- OsLocationData locationData = osLocationData("Authority", "SW1A 1AA");
- LocalAuthorityType authorityType = localAuthorityType(UUID.randomUUID());
- List results = List.of(mock(CourtWithDistance.class));
-
- when(localAuthorityTypeRepository.findIdByNameIgnoreCase("Authority"))
- .thenReturn(Optional.of(authorityType));
- when(courtAddressRepository.findFamilyRegionalByLocalAuthority(
- area.getId(),
- 51.5,
- -0.1,
- area.getAreaOfLawId(),
- authorityType.getId()
- )).thenReturn(List.of());
- when(courtAddressRepository.findFamilyRegionalByAol(area.getId(), 51.5, -0.1, area.getAreaOfLawId()))
- .thenReturn(List.of());
- when(courtAddressRepository.findNearestByAreaOfLaw(51.5, -0.1, area.getAreaOfLawId(), 10))
- .thenReturn(results);
-
- List response = searchExecuter.executeSearchStrategy(
- locationData,
- area,
- SearchStrategy.FAMILY_REGIONAL,
- SearchAction.DOCUMENTS,
- 10
- );
-
- assertThat(response).isEqualTo(results);
- }
-
@Test
void executeSearchStrategyShouldReturnFamilyNonRegionalByLocalAuthorityWhenFound() {
ServiceArea area = serviceArea(ServiceAreaType.FAMILY);
From 1ddcff4c2d2fe2557d144ad9e92ba838c375db19 Mon Sep 17 00:00:00 2001
From: Josh <23316525+josh-uk@users.noreply.github.com>
Date: Thu, 18 Jun 2026 09:29:40 +0100
Subject: [PATCH 02/40] FACT-2618 Add service centre entities and update audit
(#273)
* Add service centre entities and update audit
* jsonignore
---
.../fact/data/api/audit/AuditingTest.java | 38 ++++++
.../api/controllers/AuditControllerTest.java | 66 ++++++++++
.../audit/AuditableCourtEntityListener.java | 25 ++--
.../data/api/controllers/AuditController.java | 10 +-
.../reform/fact/data/api/entities/Audit.java | 23 +++-
.../api/entities/AuditableCourtEntity.java | 16 ++-
.../data/api/entities/AuditableEntity.java | 16 +++
.../fact/data/api/entities/ServiceCentre.java | 119 ++++++++++++++++++
.../api/entities/ServiceCentreAddress.java | 110 ++++++++++++++++
.../api/entities/ServiceCentreAreasOfLaw.java | 86 +++++++++++++
.../entities/ServiceCentreContactDetails.java | 116 +++++++++++++++++
.../api/entities/types/AuditSubjectType.java | 6 +
.../api/repositories/AuditRepository.java | 27 ++--
.../ServiceCentreAddressRepository.java | 10 ++
.../ServiceCentreAreasOfLawRepository.java | 10 ++
...ServiceCentreContactDetailsRepository.java | 10 ++
.../repositories/ServiceCentreRepository.java | 10 ++
.../fact/data/api/services/AuditService.java | 51 +++++---
.../V1.28__create_service_centre_tables.sql | 64 ++++++++++
.../V1.29__generalise_audit_subject.sql | 12 ++
.../AuditableCourtEntityListenerTest.java | 3 +
.../api/controllers/AuditControllerTest.java | 25 +++-
.../data/api/services/AuditServiceTest.java | 89 ++++++++++---
23 files changed, 881 insertions(+), 61 deletions(-)
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/AuditableEntity.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/ServiceCentre.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/ServiceCentreAddress.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/ServiceCentreAreasOfLaw.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/ServiceCentreContactDetails.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/types/AuditSubjectType.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/ServiceCentreAddressRepository.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/ServiceCentreAreasOfLawRepository.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/ServiceCentreContactDetailsRepository.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/ServiceCentreRepository.java
create mode 100644 src/main/resources/db/migration/V1.28__create_service_centre_tables.sql
create mode 100644 src/main/resources/db/migration/V1.29__generalise_audit_subject.sql
diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditingTest.java b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditingTest.java
index 4ea5d0c1e..3891496cb 100644
--- a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditingTest.java
+++ b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditingTest.java
@@ -8,14 +8,17 @@
import uk.gov.hmcts.reform.fact.data.api.entities.CourtPhoto;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtTranslation;
import uk.gov.hmcts.reform.fact.data.api.entities.Region;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentre;
import uk.gov.hmcts.reform.fact.data.api.entities.User;
import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditActionType;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditSubjectType;
import uk.gov.hmcts.reform.fact.data.api.entities.types.UserRole;
import uk.gov.hmcts.reform.fact.data.api.repositories.AuditRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtPhotoRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtTranslationRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.RegionRepository;
+import uk.gov.hmcts.reform.fact.data.api.repositories.ServiceCentreRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.UserRepository;
import uk.gov.hmcts.reform.fact.data.api.services.AuditService;
@@ -63,6 +66,9 @@ class AuditingTest {
@Autowired
CourtPhotoRepository courtPhotoRepository;
+ @Autowired
+ ServiceCentreRepository serviceCentreRepository;
+
@Autowired
AuditRepository auditRepository;
@@ -83,6 +89,7 @@ void setUp() {
courtRepository.deleteAll();
courtPhotoRepository.deleteAll();
courtTranslationRepository.deleteAll();
+ serviceCentreRepository.deleteAll();
// do this one last
auditRepository.deleteAll();
@@ -120,6 +127,36 @@ void deletingExistingAuditRecordsViaServiceShouldWork() {
assertEquals(0L, auditRepository.count());
}
+ @Test
+ @DisplayName("Creating and updating a ServiceCentre should create Audit records")
+ void creatingAndUpdatingAServiceCentreShouldCreateAuditRecords() {
+ ServiceCentre serviceCentre = ServiceCentre.builder()
+ .name("Test Service Centre")
+ .slug("test-service-centre")
+ .open(Boolean.FALSE)
+ .build();
+ serviceCentreRepository.save(serviceCentre);
+
+ serviceCentre.setOpen(Boolean.TRUE);
+ serviceCentreRepository.save(serviceCentre);
+
+ Page audits = auditRepository.findBySubjectIdAndSubjectTypeAndCreatedAtAfter(
+ serviceCentre.getId(),
+ AuditSubjectType.SERVICE_CENTRE,
+ CREATED_AFTER.atStartOfDay().atZone(java.time.ZoneOffset.UTC),
+ org.springframework.data.domain.PageRequest.of(0, 1000)
+ );
+
+ List content = audits.getContent().stream().filter(audit -> Objects.equals(
+ audit.getActionEntity(),
+ ServiceCentre.class.getSimpleName()
+ )).toList();
+
+ assertEquals(2, content.size());
+ assertEquals(1, content.stream().filter(audit -> AuditActionType.UPDATE.equals(audit.getActionType())).count());
+ assertEquals(1, content.stream().filter(audit -> AuditActionType.INSERT.equals(audit.getActionType())).count());
+ }
+
@Test
@DisplayName("Creating and updating a CourtTranslation should create Audit records")
void creatingAndUpdatingACourtTranslationShouldCreateAuditRecords() {
@@ -330,6 +367,7 @@ private void assertAuditActionsForEntity(Court owningCourt, String testedEntity,
CREATED_AFTER,
null,
owningCourt.getId().toString(),
+ null,
null
);
diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/AuditControllerTest.java b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/AuditControllerTest.java
index 8bc737ebc..0e2fb6c0d 100644
--- a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/AuditControllerTest.java
+++ b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/AuditControllerTest.java
@@ -8,12 +8,14 @@
import uk.gov.hmcts.reform.fact.data.api.audit.AuditUserContext;
import uk.gov.hmcts.reform.fact.data.api.entities.Court;
import uk.gov.hmcts.reform.fact.data.api.entities.Region;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentre;
import uk.gov.hmcts.reform.fact.data.api.entities.User;
import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditActionType;
import uk.gov.hmcts.reform.fact.data.api.entities.types.UserRole;
import uk.gov.hmcts.reform.fact.data.api.repositories.AuditRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.CourtRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.RegionRepository;
+import uk.gov.hmcts.reform.fact.data.api.repositories.ServiceCentreRepository;
import uk.gov.hmcts.reform.fact.data.api.repositories.UserRepository;
import java.time.LocalDate;
@@ -55,6 +57,9 @@ class AuditControllerTest {
@Autowired
AuditRepository auditRepository;
+ @Autowired
+ ServiceCentreRepository serviceCentreRepository;
+
@Autowired
UserRepository userRepository;
@@ -73,6 +78,7 @@ void setUp() {
regions = regionRepository.findAll();
// and clear these down
courtRepository.deleteAll();
+ serviceCentreRepository.deleteAll();
auditRepository.deleteAll();
auditUserContext.clear();
@@ -181,6 +187,36 @@ void getFilteredAndPaginatedAuditsReturnsFilteredResults() throws Exception {
.andExpect(jsonPath("$.page.totalElements").value(3));
}
+ @Test
+ @DisplayName("GET /audits/v1 returns results filtered by serviceCentreId")
+ void getFilteredAndPaginatedAuditsReturnsServiceCentreFilteredResults() throws Exception {
+
+ ServiceCentre serviceCentre1 = createTestServiceCentre();
+ for (int i = 0; i < 2; i++) {
+ serviceCentre1.setOpen(!Optional.ofNullable(serviceCentre1.getOpen()).orElse(false));
+ serviceCentreRepository.save(serviceCentre1);
+ }
+
+ ServiceCentre serviceCentre2 = createTestServiceCentre();
+ serviceCentre2.setOpen(!Optional.ofNullable(serviceCentre2.getOpen()).orElse(false));
+ serviceCentreRepository.save(serviceCentre2);
+
+ mvc.perform(get("/audits/v1")
+ .param("pageNumber", "0")
+ .param("pageSize", "10")
+ .param("fromDate", LocalDate.now().toString())
+ .param("serviceCentreId", serviceCentre1.getId().toString()))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.content").isArray())
+ .andExpect(jsonPath("$.content.length()").value(3))
+ .andExpect(jsonPath("$.content[0]").exists())
+ .andExpect(jsonPath("$.content[*].subjectId").value(allElementsEqual(serviceCentre1.getId().toString())))
+ .andExpect(jsonPath("$.content[*].subjectType").value(allElementsEqual("SERVICE_CENTRE")))
+ .andExpect(jsonPath("$.page.number").value(0))
+ .andExpect(jsonPath("$.page.totalPages").value(1))
+ .andExpect(jsonPath("$.page.totalElements").value(3));
+ }
+
@Test
@DisplayName("GET /audits/v1 returns correct action types")
@@ -262,6 +298,29 @@ void getFilteredAndPaginatedAuditsReturnsBadRequestForInvalidCourtId() throws Ex
.andExpect(status().isBadRequest());
}
+ @Test
+ @DisplayName("GET /audits/v1 returns returns 400 when serviceCentreId is invalid")
+ void getFilteredAndPaginatedAuditsReturnsBadRequestForInvalidServiceCentreId() throws Exception {
+ mvc.perform(get("/audits/v1")
+ .param("pageNumber", "0")
+ .param("pageSize", "5")
+ .param("fromDate", LocalDate.now().toString())
+ .param("serviceCentreId", "this-is-not-a-valid-service-centre-id"))
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ @DisplayName("GET /audits/v1 returns returns 400 when courtId and serviceCentreId are both provided")
+ void getFilteredAndPaginatedAuditsReturnsBadRequestForBothSubjectIds() throws Exception {
+ mvc.perform(get("/audits/v1")
+ .param("pageNumber", "0")
+ .param("pageSize", "5")
+ .param("fromDate", LocalDate.now().toString())
+ .param("courtId", UUID.randomUUID().toString())
+ .param("serviceCentreId", UUID.randomUUID().toString()))
+ .andExpect(status().isBadRequest());
+ }
+
@Test
@DisplayName("GET /audits/v1 returns returns 400 when page size is invalid")
void getFilteredAndPaginatedAuditsReturnsBadRequestForInvalidPageSize() throws Exception {
@@ -301,6 +360,13 @@ private List createTestCourts(int num) {
return results;
}
+ private ServiceCentre createTestServiceCentre() {
+ return serviceCentreRepository.save(ServiceCentre.builder()
+ .name("Service Centre " + RandomStringUtils.insecure().next(10, true, false))
+ .open(Boolean.FALSE)
+ .build());
+ }
+
private Court buildCourt(UUID regionId, String name) {
return Court.builder()
.name(name)
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditableCourtEntityListener.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditableCourtEntityListener.java
index d693be26e..6c8079fa9 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditableCourtEntityListener.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditableCourtEntityListener.java
@@ -1,7 +1,7 @@
package uk.gov.hmcts.reform.fact.data.api.audit;
import uk.gov.hmcts.reform.fact.data.api.entities.Audit;
-import uk.gov.hmcts.reform.fact.data.api.entities.AuditableCourtEntity;
+import uk.gov.hmcts.reform.fact.data.api.entities.AuditableEntity;
import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditActionType;
import uk.gov.hmcts.reform.fact.data.api.entities.types.Change;
@@ -33,7 +33,7 @@
import org.springframework.stereotype.Component;
/**
- * Entity listener implementation for all {@link AuditableCourtEntity} derived entities.
+ * Entity listener implementation for all {@link AuditableEntity} derived entities.
*/
@Component
@RequiredArgsConstructor
@@ -60,17 +60,17 @@ public void setApplicationContext(final ApplicationContext applicationContext) t
}
@PrePersist
- public void beforePersist(AuditableCourtEntity entity) {
+ public void beforePersist(AuditableEntity entity) {
writeAudit(entity, AuditActionType.INSERT);
}
@PreUpdate
- public void beforeUpdate(AuditableCourtEntity entity) {
+ public void beforeUpdate(AuditableEntity entity) {
writeAudit(entity, AuditActionType.UPDATE);
}
@PreRemove
- public void beforeRemove(AuditableCourtEntity entity) {
+ public void beforeRemove(AuditableEntity entity) {
writeAudit(entity, AuditActionType.DELETE);
}
@@ -92,13 +92,13 @@ private boolean ensureAuditUserContext() {
return auditUserContextRef.get() != null;
}
- private void writeAudit(AuditableCourtEntity entity, AuditActionType auditActionType) {
+ private void writeAudit(AuditableEntity entity, AuditActionType auditActionType) {
if (ensureAuditUserContext() && auditUserContextRef.get().isAuditSuppressed()) {
return;
}
if (ensureEntityManager()) {
- AuditableCourtEntity previousEntity = auditActionType == AuditActionType.INSERT
+ AuditableEntity previousEntity = auditActionType == AuditActionType.INSERT
? null
: findPreviousEntity(entity);
writeAuditRecord(entity, previousEntity, auditActionType);
@@ -108,14 +108,14 @@ private void writeAudit(AuditableCourtEntity entity, AuditActionType auditAction
}
}
- private AuditableCourtEntity findPreviousEntity(final AuditableCourtEntity entity) {
+ private AuditableEntity findPreviousEntity(final AuditableEntity entity) {
// Need to run our lookup for the previous entity on a different
// thread. If we don't, JPA/Hibernate will intercept and return the
// data that's about to be persisted as part of the event we're
// listening to.
- Future future = executor.submit(
+ Future future = executor.submit(
() -> entityManagerRef.get().find(
entity.getClass(),
entity.getId()
@@ -135,7 +135,7 @@ private AuditableCourtEntity findPreviousEntity(final AuditableCourtEntity entit
}
}
- private void writeAuditRecord(AuditableCourtEntity entity, AuditableCourtEntity previous,
+ private void writeAuditRecord(AuditableEntity entity, AuditableEntity previous,
AuditActionType operationType) {
if (auditUserContextRef.get() == null && !ensureAuditUserContext()) {
log.error("No audit user context available during an audit operation");
@@ -143,7 +143,8 @@ private void writeAuditRecord(AuditableCourtEntity entity, AuditableCourtEntity
}
Audit.AuditBuilder audit = Audit.builder()
- .courtId(entity.getCourtId())
+ .subjectId(entity.getAuditSubjectId())
+ .subjectType(entity.getAuditSubjectType())
.actionType(operationType)
.actionEntity(entity.getClass().getSimpleName())
.createdAt(ZonedDateTime.now())
@@ -154,7 +155,7 @@ private void writeAuditRecord(AuditableCourtEntity entity, AuditableCourtEntity
entityManagerRef.get().persist(audit.build());
}
- private List generateDiffs(AuditableCourtEntity previous, AuditableCourtEntity current) {
+ private List generateDiffs(AuditableEntity previous, AuditableEntity current) {
List diffs = new ArrayList<>();
try {
// convert the entities into maps
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/AuditController.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/AuditController.java
index 66f0e4169..b951e04b6 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/AuditController.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/AuditController.java
@@ -2,6 +2,7 @@
import uk.gov.hmcts.reform.fact.data.api.entities.Audit;
import uk.gov.hmcts.reform.fact.data.api.errorhandling.exceptions.InvalidDateRangeException;
+import uk.gov.hmcts.reform.fact.data.api.errorhandling.exceptions.InvalidParameterCombinationException;
import uk.gov.hmcts.reform.fact.data.api.security.SecuredFactRestController;
import uk.gov.hmcts.reform.fact.data.api.services.AuditService;
import uk.gov.hmcts.reform.fact.data.api.validation.annotations.ValidUUID;
@@ -38,9 +39,9 @@ public class AuditController {
@GetMapping("/v1")
@Operation(
- summary = "Get filtered and paginated list of audits for a given Court",
+ summary = "Get filtered and paginated list of audits",
description = "Fetch a paginated and optionally filtered, list of audit records"
- + " that relate to a specific Court"
+ + " that relate to a specific Court or Service Centre"
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved list of audits"),
@@ -52,6 +53,7 @@ public ResponseEntity> getFilteredAndPaginatedAudits(
@RequestParam(name = "pageSize", defaultValue = "25")
@Positive(message = "pageSize must be greater than 0") int pageSize,
@RequestParam(name = "courtId", required = false) @ValidUUID(allowNull = true) String courtId,
+ @RequestParam(name = "serviceCentreId", required = false) @ValidUUID(allowNull = true) String serviceCentreId,
@RequestParam(name = "email", required = false)
@Pattern(
regexp = "^[A-Za-z0-9._+-]*(|@[A-Za-z0-9._+-]*)$",
@@ -68,6 +70,9 @@ public ResponseEntity> getFilteredAndPaginatedAudits(
if (toDate != null && toDate.isBefore(fromDate)) {
throw new InvalidDateRangeException("toDate must not be before fromDate");
}
+ if (courtId != null && serviceCentreId != null) {
+ throw new InvalidParameterCombinationException("Only one of courtId or serviceCentreId can be provided");
+ }
return ResponseEntity.ok(
auditService.getFilteredAndPaginatedAudits(
@@ -76,6 +81,7 @@ public ResponseEntity> getFilteredAndPaginatedAudits(
fromDate,
toDate,
courtId,
+ serviceCentreId,
emailMatch
)
);
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/Audit.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/Audit.java
index e707a75db..abbfd73ee 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/Audit.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/Audit.java
@@ -1,12 +1,14 @@
package uk.gov.hmcts.reform.fact.data.api.entities;
import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditActionType;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditSubjectType;
import uk.gov.hmcts.reform.fact.data.api.entities.types.Change;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
+import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -48,12 +50,16 @@ public class Audit {
private UUID id;
@NotNull
- @Column(name = "court_id")
- private UUID courtId;
+ @Column(name = "subject_id")
+ private UUID subjectId;
+
+ @NotNull
+ @Enumerated(EnumType.STRING)
+ private AuditSubjectType subjectType;
@Schema(description = "The associated Court")
@ManyToOne
- @JoinColumn(name = "court_id", insertable = false, updatable = false)
+ @JoinColumn(name = "subject_id", insertable = false, updatable = false)
private Court court;
@NotNull
@@ -83,4 +89,15 @@ public class Audit {
@Setter(AccessLevel.NONE)
private ZonedDateTime createdAt;
+ @JsonProperty("courtId")
+ public UUID getCourtId() {
+ return AuditSubjectType.COURT.equals(subjectType) ? subjectId : null;
+ }
+
+ @JsonProperty("courtId")
+ public void setCourtId(UUID courtId) {
+ this.subjectId = courtId;
+ this.subjectType = AuditSubjectType.COURT;
+ }
+
}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/AuditableCourtEntity.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/AuditableCourtEntity.java
index 424b66d35..1cf6f5766 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/AuditableCourtEntity.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/AuditableCourtEntity.java
@@ -1,9 +1,19 @@
package uk.gov.hmcts.reform.fact.data.api.entities;
-import java.util.UUID;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditSubjectType;
-public interface AuditableCourtEntity {
- UUID getId();
+import java.util.UUID;
+public interface AuditableCourtEntity extends AuditableEntity {
UUID getCourtId();
+
+ @Override
+ default UUID getAuditSubjectId() {
+ return getCourtId();
+ }
+
+ @Override
+ default AuditSubjectType getAuditSubjectType() {
+ return AuditSubjectType.COURT;
+ }
}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/AuditableEntity.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/AuditableEntity.java
new file mode 100644
index 000000000..d1438f40f
--- /dev/null
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/AuditableEntity.java
@@ -0,0 +1,16 @@
+package uk.gov.hmcts.reform.fact.data.api.entities;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditSubjectType;
+
+import java.util.UUID;
+
+public interface AuditableEntity {
+ UUID getId();
+
+ @JsonIgnore
+ UUID getAuditSubjectId();
+
+ @JsonIgnore
+ AuditSubjectType getAuditSubjectType();
+}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/ServiceCentre.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/ServiceCentre.java
new file mode 100644
index 000000000..b938e8e33
--- /dev/null
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/ServiceCentre.java
@@ -0,0 +1,119 @@
+package uk.gov.hmcts.reform.fact.data.api.entities;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EntityListeners;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.experimental.SuperBuilder;
+import org.hibernate.annotations.CreationTimestamp;
+import org.hibernate.annotations.JdbcTypeCode;
+import org.hibernate.annotations.TimeZoneStorage;
+import org.hibernate.annotations.TimeZoneStorageType;
+import org.hibernate.annotations.UpdateTimestamp;
+import org.hibernate.type.SqlTypes;
+import uk.gov.hmcts.reform.fact.data.api.audit.AuditableCourtEntityListener;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditSubjectType;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.CatchmentType;
+import uk.gov.hmcts.reform.fact.data.api.validation.annotations.ValidCourtSlug;
+
+import java.time.ZonedDateTime;
+import java.util.List;
+import java.util.UUID;
+
+@Data
+@SuperBuilder
+@NoArgsConstructor
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+@Entity
+@EntityListeners(AuditableCourtEntityListener.class)
+@Table(name = "service_centre")
+public class ServiceCentre implements AuditableEntity {
+
+ @Schema(
+ description = "The internal ID - assigned by the server during creation",
+ accessMode = Schema.AccessMode.READ_ONLY
+ )
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private UUID id;
+
+ @Schema(description = "The name of the Service Centre")
+ @NotBlank(message = "Service centre name must be specified")
+ @Size(min = 5, max = 200, message = "Service centre name should be between 5 and 200 chars")
+ @Pattern(
+ regexp = "^[A-Za-z&'()\\- ]+$",
+ message = "Service centre name may only contain letters, spaces, apostrophes, hyphens, ampersands, "
+ + "and parentheses"
+ )
+ private String name;
+
+ @Schema(description = "The Service Centre 'slug'")
+ @ValidCourtSlug
+ @Column(unique = true)
+ private String slug;
+
+ @Schema(description = "The open status of the Service Centre")
+ @NotNull(message = "Service centre open status must be specified")
+ private Boolean open;
+
+ @Schema(description = "Any warning notices attached to the Service Centre")
+ @Size(max = 500, message = "Warning notice must be less than 500 characters")
+ @Pattern(
+ regexp = "^[A-Za-z0-9.,!?:;'\"()\\-/&@+\\s]+$",
+ message = "Warning notice may only contain letters, numbers, spaces, and standard punctuation or symbols (@, +)"
+ )
+ @Column(name = "warning_notice")
+ private String warningNotice;
+
+ @Schema(
+ description = "The created date/time of the Service Centre record",
+ accessMode = Schema.AccessMode.READ_ONLY
+ )
+ @TimeZoneStorage(TimeZoneStorageType.NORMALIZE_UTC)
+ @CreationTimestamp
+ @Setter(AccessLevel.NONE)
+ private ZonedDateTime createdAt;
+
+ @Schema(
+ description = "The last updated date/time of the Service Centre record",
+ accessMode = Schema.AccessMode.READ_ONLY
+ )
+ @TimeZoneStorage(TimeZoneStorageType.NORMALIZE_UTC)
+ @UpdateTimestamp
+ @Setter(AccessLevel.NONE)
+ private ZonedDateTime lastUpdatedAt;
+
+ @Schema(description = "The list of associated Service Area IDs")
+ @JdbcTypeCode(SqlTypes.ARRAY)
+ @Column(columnDefinition = "uuid[]")
+ private List serviceAreaIds;
+
+ @Schema(description = "The catchment type")
+ @Enumerated(EnumType.STRING)
+ private CatchmentType catchmentType;
+
+ @Override
+ public UUID getAuditSubjectId() {
+ return id;
+ }
+
+ @Override
+ public AuditSubjectType getAuditSubjectType() {
+ return AuditSubjectType.SERVICE_CENTRE;
+ }
+}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/ServiceCentreAddress.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/ServiceCentreAddress.java
new file mode 100644
index 000000000..71afc8a87
--- /dev/null
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/ServiceCentreAddress.java
@@ -0,0 +1,110 @@
+package uk.gov.hmcts.reform.fact.data.api.entities;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EntityListeners;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import uk.gov.hmcts.reform.fact.data.api.audit.AuditableCourtEntityListener;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditSubjectType;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.AddressType;
+import uk.gov.hmcts.reform.fact.data.api.entities.validation.ValidationConstants;
+import uk.gov.hmcts.reform.fact.data.api.validation.annotations.ValidPostcode;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+@Data
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+@NoArgsConstructor
+@Builder
+@Entity
+@EntityListeners(AuditableCourtEntityListener.class)
+@Table(name = "service_centre_address")
+public class ServiceCentreAddress implements AuditableEntity {
+
+ @Schema(
+ description = "The internal ID - assigned by the server during creation",
+ accessMode = Schema.AccessMode.READ_ONLY
+ )
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private UUID id;
+
+ @Schema(description = "The ID of the associated Service Centre", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull
+ @Column(name = "service_centre_id")
+ private UUID serviceCentreId;
+
+ @JsonIgnore
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "service_centre_id", insertable = false, updatable = false)
+ private ServiceCentre serviceCentre;
+
+ @Schema(description = "The first address line")
+ @Size(max = 255, message = "Address line should be 255 characters or less")
+ @Pattern(regexp = ValidationConstants.OPTIONAL_ADDRESS_LINE_REGEX,
+ message = "Address Line 1: " + ValidationConstants.ADDRESS_LINE_REGEX_MESSAGE)
+ @Column(name = "address_line_1")
+ private String addressLine1;
+
+ @Schema(description = "The second address line")
+ @Size(max = 255, message = "Address line should be 255 characters or less")
+ @Pattern(regexp = ValidationConstants.OPTIONAL_ADDRESS_LINE_REGEX,
+ message = "Address Line 2: " + ValidationConstants.ADDRESS_LINE_REGEX_MESSAGE)
+ @Column(name = "address_line_2")
+ private String addressLine2;
+
+ @Schema(description = "The town/city")
+ @Size(max = 100, message = "Town/City name should be 100 characters or less")
+ @Pattern(regexp = ValidationConstants.OPTIONAL_ADDRESS_LINE_REGEX,
+ message = "Town/City: " + ValidationConstants.ADDRESS_LINE_REGEX_MESSAGE)
+ private String townCity;
+
+ @Schema(description = "The county")
+ @Size(max = 100, message = "County name should be 100 characters or less")
+ @Pattern(regexp = ValidationConstants.OPTIONAL_ADDRESS_LINE_REGEX,
+ message = "County: " + ValidationConstants.ADDRESS_LINE_REGEX_MESSAGE)
+ private String county;
+
+ @Schema(description = "The postcode")
+ @ValidPostcode
+ private String postcode;
+
+ @Schema(description = "The latitude coordinate")
+ private BigDecimal lat;
+
+ @Schema(description = "The longitude coordinate")
+ private BigDecimal lon;
+
+ @Schema(description = "The address type")
+ @Enumerated(EnumType.STRING)
+ private AddressType addressType;
+
+ @Override
+ public UUID getAuditSubjectId() {
+ return serviceCentreId;
+ }
+
+ @Override
+ public AuditSubjectType getAuditSubjectType() {
+ return AuditSubjectType.SERVICE_CENTRE;
+ }
+}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/ServiceCentreAreasOfLaw.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/ServiceCentreAreasOfLaw.java
new file mode 100644
index 000000000..7c04100c8
--- /dev/null
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/ServiceCentreAreasOfLaw.java
@@ -0,0 +1,86 @@
+package uk.gov.hmcts.reform.fact.data.api.entities;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EntityListeners;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+import jakarta.persistence.Transient;
+import jakarta.validation.constraints.NotNull;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.hibernate.annotations.JdbcTypeCode;
+import org.hibernate.type.SqlTypes;
+import uk.gov.hmcts.reform.fact.data.api.audit.AuditableCourtEntityListener;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditSubjectType;
+
+import java.util.List;
+import java.util.UUID;
+
+@Data
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+@NoArgsConstructor
+@Builder
+@Entity
+@EntityListeners(AuditableCourtEntityListener.class)
+@Table(name = "service_centre_areas_of_law")
+public class ServiceCentreAreasOfLaw implements AuditableEntity {
+
+ @Schema(
+ description = "The internal ID - assigned by the server during creation",
+ accessMode = Schema.AccessMode.READ_ONLY
+ )
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private UUID id;
+
+ @Schema(description = "The ID of the associated Service Centre", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull
+ @Column(name = "service_centre_id")
+ private UUID serviceCentreId;
+
+ @JsonIgnore
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "service_centre_id", insertable = false, updatable = false)
+ private ServiceCentre serviceCentre;
+
+ @Schema(description = "The list of associated Areas of Law")
+ @JdbcTypeCode(SqlTypes.ARRAY)
+ @Column(columnDefinition = "uuid[]")
+ private List areasOfLaw;
+
+ @Transient
+ @JsonIgnore
+ private List areasOfLawDetails;
+
+ @JsonProperty("areasOfLaw")
+ public List> getAreasOfLawForView() {
+ return areasOfLawDetails != null ? areasOfLawDetails : areasOfLaw;
+ }
+
+ @JsonIgnore
+ public List getAreasOfLaw() {
+ return areasOfLaw;
+ }
+
+ @Override
+ public UUID getAuditSubjectId() {
+ return serviceCentreId;
+ }
+
+ @Override
+ public AuditSubjectType getAuditSubjectType() {
+ return AuditSubjectType.SERVICE_CENTRE;
+ }
+}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/ServiceCentreContactDetails.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/ServiceCentreContactDetails.java
new file mode 100644
index 000000000..43ac8f7e3
--- /dev/null
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/ServiceCentreContactDetails.java
@@ -0,0 +1,116 @@
+package uk.gov.hmcts.reform.fact.data.api.entities;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EntityListeners;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+import jakarta.persistence.Transient;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import uk.gov.hmcts.reform.fact.data.api.audit.AuditableCourtEntityListener;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditSubjectType;
+import uk.gov.hmcts.reform.fact.data.api.entities.validation.ValidationConstants;
+
+import java.util.UUID;
+
+@Data
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+@NoArgsConstructor
+@Builder
+@Entity
+@EntityListeners(AuditableCourtEntityListener.class)
+@Table(name = "service_centre_contact_details")
+public class ServiceCentreContactDetails implements AuditableEntity {
+
+ @Schema(
+ description = "The internal ID - assigned by the server during creation",
+ accessMode = Schema.AccessMode.READ_ONLY
+ )
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private UUID id;
+
+ @Schema(description = "The ID of the associated Service Centre", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull
+ @Column(name = "service_centre_id")
+ private UUID serviceCentreId;
+
+ @JsonIgnore
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "service_centre_id", insertable = false, updatable = false)
+ private ServiceCentre serviceCentre;
+
+ @Schema(description = "The ID of the associated Contact Description Type")
+ @Column(name = "service_centre_contact_description_id")
+ private UUID serviceCentreContactDescriptionId;
+
+ @JsonIgnore
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "service_centre_contact_description_id", insertable = false, updatable = false)
+ private ContactDescriptionType serviceCentreContactDescription;
+
+ @Transient
+ @JsonIgnore
+ private ContactDescriptionType serviceCentreContactDescriptionDetails;
+
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ @JsonProperty("serviceCentreContactDescription")
+ public ContactDescriptionType getServiceCentreContactDescriptionForView() {
+ return serviceCentreContactDescriptionDetails;
+ }
+
+ @Schema(description = "The explanation")
+ @Column(name = "explanation", length = 250)
+ @Size(max = 250, message = "Explanation should be no more than {max} characters")
+ @Pattern(
+ regexp = "^[A-Za-z0-9 '\\-()&+]*$",
+ message = "Explanation contains invalid characters. Allowed: letters, numbers, spaces, apostrophes, - ( ) & +"
+ )
+ private String explanation;
+
+ @Schema(description = "The Welsh language explanation")
+ @Column(name = "explanation_cy", length = 250)
+ @Size(max = 250, message = "Explanation should be no more than {max} characters")
+ @Pattern(
+ regexp = "^[\\p{L}0-9 '\\-()&+]*$",
+ message = "Welsh explanation contains invalid characters. Allowed: letters (with accents), numbers, spaces, "
+ + "apostrophes, - ( ) & +"
+ )
+ private String explanationCy;
+
+ @Schema(description = "The associated email address")
+ @Size(max = ValidationConstants.EMAIL_MAX_LENGTH, message = ValidationConstants.EMAIL_MAX_LENGTH_MESSAGE)
+ @Pattern(regexp = ValidationConstants.EMAIL_REGEX, message = ValidationConstants.EMAIL_REGEX_MESSAGE)
+ private String email;
+
+ @Schema(description = "The associated phone number")
+ @Size(max = ValidationConstants.PHONE_NO_MAX_LENGTH, message = ValidationConstants.PHONE_NO_MAX_LENGTH_MESSAGE)
+ @Pattern(regexp = ValidationConstants.PHONE_NO_REGEX, message = ValidationConstants.PHONE_NO_REGEX_MESSAGE)
+ private String phoneNumber;
+
+ @Override
+ public UUID getAuditSubjectId() {
+ return serviceCentreId;
+ }
+
+ @Override
+ public AuditSubjectType getAuditSubjectType() {
+ return AuditSubjectType.SERVICE_CENTRE;
+ }
+}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/types/AuditSubjectType.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/types/AuditSubjectType.java
new file mode 100644
index 000000000..6d97135cf
--- /dev/null
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/entities/types/AuditSubjectType.java
@@ -0,0 +1,6 @@
+package uk.gov.hmcts.reform.fact.data.api.entities.types;
+
+public enum AuditSubjectType {
+ COURT,
+ SERVICE_CENTRE
+}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/AuditRepository.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/AuditRepository.java
index 7f49759b2..daf2df1b7 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/AuditRepository.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/AuditRepository.java
@@ -1,6 +1,7 @@
package uk.gov.hmcts.reform.fact.data.api.repositories;
import uk.gov.hmcts.reform.fact.data.api.entities.Audit;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditSubjectType;
import java.time.ZonedDateTime;
import java.util.UUID;
@@ -26,13 +27,15 @@ Page findByCreatedAtBetween(
ZonedDateTime createdAtBefore,
Pageable pageable);
- Page findByCourtIdAndCreatedAtAfter(
- UUID courtId,
+ Page findBySubjectIdAndSubjectTypeAndCreatedAtAfter(
+ UUID subjectId,
+ AuditSubjectType subjectType,
ZonedDateTime createdAt,
Pageable pageable);
- Page findByCourtIdAndCreatedAtBetween(
- UUID courtId,
+ Page findBySubjectIdAndSubjectTypeAndCreatedAtBetween(
+ UUID subjectId,
+ AuditSubjectType subjectType,
ZonedDateTime createdAtAfter,
ZonedDateTime createdAtBefore,
Pageable pageable);
@@ -61,7 +64,7 @@ Page findByCourtIdAndCreatedAtBetween(
and u.email like %:email%
""";
- String AND_COURT_ID_EQUALS = " and a.courtId = :courtId";
+ String AND_SUBJECT_EQUALS = " and a.subjectId = :subjectId and a.subjectType = :subjectType";
@Query(value = SELECT_WITH_USER_JOIN + WHERE_EMAIL_LIKE_AND_CREATED_AT_AFTER)
@@ -77,16 +80,18 @@ Page findByCreatedAtBetweenAndEmailAddressLike(
String email,
Pageable pageable);
- @Query(value = SELECT_WITH_USER_JOIN + WHERE_EMAIL_LIKE_AND_CREATED_AT_AFTER + AND_COURT_ID_EQUALS)
- Page findByCourtIdAndCreatedAtAfterAndEmailAddressLike(
- UUID courtId,
+ @Query(value = SELECT_WITH_USER_JOIN + WHERE_EMAIL_LIKE_AND_CREATED_AT_AFTER + AND_SUBJECT_EQUALS)
+ Page findBySubjectIdAndSubjectTypeAndCreatedAtAfterAndEmailAddressLike(
+ UUID subjectId,
+ AuditSubjectType subjectType,
ZonedDateTime createdAtAfter,
String email,
Pageable pageable);
- @Query(value = SELECT_WITH_USER_JOIN + WHERE_EMAIL_LIKE_AND_CREATED_AT_BETWEEN + AND_COURT_ID_EQUALS)
- Page findByCourtIdAndCreatedAtBetweenAndEmailAddressLike(
- UUID courtId,
+ @Query(value = SELECT_WITH_USER_JOIN + WHERE_EMAIL_LIKE_AND_CREATED_AT_BETWEEN + AND_SUBJECT_EQUALS)
+ Page findBySubjectIdAndSubjectTypeAndCreatedAtBetweenAndEmailAddressLike(
+ UUID subjectId,
+ AuditSubjectType subjectType,
ZonedDateTime createdAtAfter,
ZonedDateTime createdAtBefore,
String email,
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/ServiceCentreAddressRepository.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/ServiceCentreAddressRepository.java
new file mode 100644
index 000000000..79886188d
--- /dev/null
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/ServiceCentreAddressRepository.java
@@ -0,0 +1,10 @@
+package uk.gov.hmcts.reform.fact.data.api.repositories;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentreAddress;
+
+import java.util.UUID;
+
+@Repository
+public interface ServiceCentreAddressRepository extends JpaRepository {}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/ServiceCentreAreasOfLawRepository.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/ServiceCentreAreasOfLawRepository.java
new file mode 100644
index 000000000..f7b79bc15
--- /dev/null
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/ServiceCentreAreasOfLawRepository.java
@@ -0,0 +1,10 @@
+package uk.gov.hmcts.reform.fact.data.api.repositories;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentreAreasOfLaw;
+
+import java.util.UUID;
+
+@Repository
+public interface ServiceCentreAreasOfLawRepository extends JpaRepository {}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/ServiceCentreContactDetailsRepository.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/ServiceCentreContactDetailsRepository.java
new file mode 100644
index 000000000..903ad4d1a
--- /dev/null
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/ServiceCentreContactDetailsRepository.java
@@ -0,0 +1,10 @@
+package uk.gov.hmcts.reform.fact.data.api.repositories;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentreContactDetails;
+
+import java.util.UUID;
+
+@Repository
+public interface ServiceCentreContactDetailsRepository extends JpaRepository {}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/ServiceCentreRepository.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/ServiceCentreRepository.java
new file mode 100644
index 000000000..67dfdf948
--- /dev/null
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/repositories/ServiceCentreRepository.java
@@ -0,0 +1,10 @@
+package uk.gov.hmcts.reform.fact.data.api.repositories;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentre;
+
+import java.util.UUID;
+
+@Repository
+public interface ServiceCentreRepository extends JpaRepository {}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/AuditService.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/AuditService.java
index 1c8fa71cb..459265771 100644
--- a/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/AuditService.java
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/services/AuditService.java
@@ -2,6 +2,8 @@
import uk.gov.hmcts.reform.fact.data.api.config.properties.AuditConfigurationProperties;
import uk.gov.hmcts.reform.fact.data.api.entities.Audit;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditSubjectType;
+import uk.gov.hmcts.reform.fact.data.api.errorhandling.exceptions.InvalidParameterCombinationException;
import uk.gov.hmcts.reform.fact.data.api.repositories.AuditRepository;
import java.time.LocalDate;
@@ -28,7 +30,7 @@ public class AuditService {
// bitfields for query column matching
private static final int IGNORE = 0b0000;
- private static final int INCLUDE_COURT_ID = 0b0001;
+ private static final int INCLUDE_SUBJECT_ID = 0b0001;
private static final int INCLUDE_TO_DATE = 0b0010;
private static final int INCLUDE_EMAIL = 0b0100;
@@ -39,12 +41,14 @@ public class AuditService {
* @param pageSize The size of the results page
* @param fromDate The "from" date for filtering. Filtering assumes start of day.
* @param courtId The id of the court. can be {@code null}.
+ * @param serviceCentreId The id of the service centre. can be {@code null}.
* @param email The email, or partial email of the related user. can be {@code null}.
* @param toDate The "to" date for auditing. Filtering assumes end of day. can e {@code null}.
* @return a {@link Page} of {@link Audit} results.
*/
public Page getFilteredAndPaginatedAudits(int pageNumber, int pageSize, @NonNull LocalDate fromDate,
- LocalDate toDate, String courtId, String email) {
+ LocalDate toDate, String courtId, String serviceCentreId,
+ String email) {
Pageable pageable = PageRequest.of(pageNumber, pageSize, Sort.by("createdAt").descending());
@@ -58,9 +62,9 @@ public Page getFilteredAndPaginatedAudits(int pageNumber, int pageSize, @
ZoneOffset.UTC
)).orElse(null);
- UUID courtUUID = Optional.ofNullable(courtId).map(UUID::fromString).orElse(null);
+ SubjectFilter subjectFilter = resolveSubjectFilter(courtId, serviceCentreId);
- return performAuditQuery(fromDateTime, toDateTime, courtUUID, email, pageable);
+ return performAuditQuery(fromDateTime, toDateTime, subjectFilter, email, pageable);
}
/**
@@ -77,36 +81,53 @@ public void removeExpiredAuditEntries() {
}
private Page performAuditQuery(ZonedDateTime fromDateTime, ZonedDateTime toDateTime,
- UUID courtUUID, String email, Pageable pageable) {
+ SubjectFilter subjectFilter, String email, Pageable pageable) {
// create a query bitfield that can be tested in the switch
// below using boolean arithmetic matching.
int query = 0;
- query |= courtUUID != null ? INCLUDE_COURT_ID : IGNORE;
+ query |= subjectFilter != null ? INCLUDE_SUBJECT_ID : IGNORE;
query |= toDateTime != null ? INCLUDE_TO_DATE : IGNORE;
query |= email != null && !email.isBlank() ? INCLUDE_EMAIL : IGNORE;
// perform the appropriate repository lookup based on matching
// bits in the query param
return switch (query) {
- case INCLUDE_COURT_ID -> auditRepository.findByCourtIdAndCreatedAtAfter(
- courtUUID, fromDateTime, pageable);
+ case INCLUDE_SUBJECT_ID -> auditRepository.findBySubjectIdAndSubjectTypeAndCreatedAtAfter(
+ subjectFilter.subjectId(), subjectFilter.subjectType(), fromDateTime, pageable);
case INCLUDE_TO_DATE -> auditRepository.findByCreatedAtBetween(
fromDateTime, toDateTime, pageable);
- case INCLUDE_COURT_ID | INCLUDE_TO_DATE -> auditRepository.findByCourtIdAndCreatedAtBetween(
- courtUUID, fromDateTime, toDateTime, pageable);
+ case INCLUDE_SUBJECT_ID | INCLUDE_TO_DATE ->
+ auditRepository.findBySubjectIdAndSubjectTypeAndCreatedAtBetween(
+ subjectFilter.subjectId(), subjectFilter.subjectType(), fromDateTime, toDateTime, pageable);
case INCLUDE_EMAIL -> auditRepository.findByCreatedAtAfterAndEmailAddressLike(
fromDateTime, email, pageable);
- case INCLUDE_COURT_ID | INCLUDE_EMAIL -> auditRepository.findByCourtIdAndCreatedAtAfterAndEmailAddressLike(
- courtUUID, fromDateTime, email, pageable);
+ case INCLUDE_SUBJECT_ID | INCLUDE_EMAIL ->
+ auditRepository.findBySubjectIdAndSubjectTypeAndCreatedAtAfterAndEmailAddressLike(
+ subjectFilter.subjectId(), subjectFilter.subjectType(), fromDateTime, email, pageable);
case INCLUDE_TO_DATE | INCLUDE_EMAIL -> auditRepository.findByCreatedAtBetweenAndEmailAddressLike(
fromDateTime, toDateTime, email, pageable);
- case INCLUDE_COURT_ID | INCLUDE_TO_DATE | INCLUDE_EMAIL ->
- auditRepository.findByCourtIdAndCreatedAtBetweenAndEmailAddressLike(
- courtUUID, fromDateTime, toDateTime, email, pageable);
+ case INCLUDE_SUBJECT_ID | INCLUDE_TO_DATE | INCLUDE_EMAIL ->
+ auditRepository.findBySubjectIdAndSubjectTypeAndCreatedAtBetweenAndEmailAddressLike(
+ subjectFilter.subjectId(), subjectFilter.subjectType(), fromDateTime, toDateTime, email, pageable);
// if no field match bits are set then test only using the
// mandatory fromDate.
default -> auditRepository.findByCreatedAtAfter(fromDateTime, pageable);
};
}
+
+ private static SubjectFilter resolveSubjectFilter(String courtId, String serviceCentreId) {
+ if (courtId != null && serviceCentreId != null) {
+ throw new InvalidParameterCombinationException("Only one of courtId or serviceCentreId can be provided");
+ }
+ if (courtId != null) {
+ return new SubjectFilter(UUID.fromString(courtId), AuditSubjectType.COURT);
+ }
+ if (serviceCentreId != null) {
+ return new SubjectFilter(UUID.fromString(serviceCentreId), AuditSubjectType.SERVICE_CENTRE);
+ }
+ return null;
+ }
+
+ private record SubjectFilter(UUID subjectId, AuditSubjectType subjectType) {}
}
diff --git a/src/main/resources/db/migration/V1.28__create_service_centre_tables.sql b/src/main/resources/db/migration/V1.28__create_service_centre_tables.sql
new file mode 100644
index 000000000..8a7e4c4c4
--- /dev/null
+++ b/src/main/resources/db/migration/V1.28__create_service_centre_tables.sql
@@ -0,0 +1,64 @@
+CREATE TABLE service_centre (
+ id UUID PRIMARY KEY NOT NULL,
+ name VARCHAR NOT NULL,
+ slug VARCHAR,
+ open BOOLEAN,
+ warning_notice VARCHAR,
+ created_at TIMESTAMP NOT NULL,
+ last_updated_at TIMESTAMP NOT NULL,
+ service_area_ids UUID[],
+ catchment_type VARCHAR
+);
+
+CREATE TABLE service_centre_address (
+ id UUID PRIMARY KEY NOT NULL,
+ service_centre_id UUID NOT NULL,
+ address_line_1 VARCHAR,
+ address_line_2 VARCHAR,
+ town_city VARCHAR,
+ county VARCHAR,
+ postcode VARCHAR,
+ lat DECIMAL(9,6),
+ lon DECIMAL(9,6),
+ address_type VARCHAR
+);
+
+CREATE TABLE service_centre_contact_details (
+ id UUID PRIMARY KEY NOT NULL,
+ service_centre_id UUID NOT NULL,
+ service_centre_contact_description_id UUID,
+ explanation VARCHAR,
+ explanation_cy VARCHAR,
+ email VARCHAR,
+ phone_number VARCHAR
+);
+
+CREATE TABLE service_centre_areas_of_law (
+ id UUID PRIMARY KEY NOT NULL,
+ service_centre_id UUID NOT NULL,
+ areas_of_law UUID[]
+);
+
+ALTER TABLE service_centre_address
+ADD CONSTRAINT fk_service_centre_address_service_centre
+ FOREIGN KEY (service_centre_id)
+ REFERENCES service_centre(id)
+ ON DELETE CASCADE;
+
+ALTER TABLE service_centre_contact_details
+ADD CONSTRAINT fk_service_centre_contact_details_service_centre
+ FOREIGN KEY (service_centre_id)
+ REFERENCES service_centre(id)
+ ON DELETE CASCADE;
+
+ALTER TABLE service_centre_contact_details
+ADD CONSTRAINT fk_service_centre_contact_details_description
+ FOREIGN KEY (service_centre_contact_description_id)
+ REFERENCES contact_description_types(id)
+ ON DELETE SET NULL;
+
+ALTER TABLE service_centre_areas_of_law
+ADD CONSTRAINT fk_service_centre_areas_of_law_service_centre
+ FOREIGN KEY (service_centre_id)
+ REFERENCES service_centre(id)
+ ON DELETE CASCADE;
diff --git a/src/main/resources/db/migration/V1.29__generalise_audit_subject.sql b/src/main/resources/db/migration/V1.29__generalise_audit_subject.sql
new file mode 100644
index 000000000..1e5b2c1f8
--- /dev/null
+++ b/src/main/resources/db/migration/V1.29__generalise_audit_subject.sql
@@ -0,0 +1,12 @@
+ALTER TABLE audit
+ RENAME COLUMN court_id TO subject_id;
+
+ALTER TABLE audit
+ ADD COLUMN subject_type VARCHAR;
+
+UPDATE audit
+SET subject_type = 'COURT'
+WHERE subject_type IS NULL;
+
+ALTER TABLE audit
+ ALTER COLUMN subject_type SET NOT NULL;
diff --git a/src/test/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditableCourtEntityListenerTest.java b/src/test/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditableCourtEntityListenerTest.java
index 51c345156..ab915c3f2 100644
--- a/src/test/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditableCourtEntityListenerTest.java
+++ b/src/test/java/uk/gov/hmcts/reform/fact/data/api/audit/AuditableCourtEntityListenerTest.java
@@ -10,6 +10,7 @@
import uk.gov.hmcts.reform.fact.data.api.entities.Audit;
import uk.gov.hmcts.reform.fact.data.api.entities.Court;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditSubjectType;
import java.util.UUID;
@@ -118,6 +119,8 @@ void generateDiffsShouldReturnChangesWhenValuesDiffer() throws Exception {
verify(entityManager, times(1)).find(Court.class, courtCurrent.getId());
Audit audit = auditCaptor.getValue();
assertEquals(USER_ID, audit.getUserId());
+ assertEquals(COURT_ID, audit.getSubjectId());
+ assertEquals(AuditSubjectType.COURT, audit.getSubjectType());
assertEquals(1, audit.getActionDataDiff().size());
assertEquals("name", audit.getActionDataDiff().getFirst().field());
}
diff --git a/src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/AuditControllerTest.java b/src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/AuditControllerTest.java
index b82facf62..4b7883c7a 100644
--- a/src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/AuditControllerTest.java
+++ b/src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/AuditControllerTest.java
@@ -9,8 +9,10 @@
import uk.gov.hmcts.reform.fact.data.api.entities.Audit;
import uk.gov.hmcts.reform.fact.data.api.entities.Court;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditSubjectType;
import uk.gov.hmcts.reform.fact.data.api.entities.types.Change;
import uk.gov.hmcts.reform.fact.data.api.errorhandling.exceptions.InvalidDateRangeException;
+import uk.gov.hmcts.reform.fact.data.api.errorhandling.exceptions.InvalidParameterCombinationException;
import uk.gov.hmcts.reform.fact.data.api.services.AuditService;
import java.time.LocalDate;
@@ -33,6 +35,7 @@ class AuditControllerTest {
private static final UUID AUDIT_ID = UUID.randomUUID();
private static final UUID COURT_ID = UUID.randomUUID();
+ private static final UUID SERVICE_CENTRE_ID = UUID.randomUUID();
private static final UUID USER_ID = UUID.randomUUID();
private static final int PAGE_NUMBER = 0;
@@ -58,6 +61,7 @@ void getFilteredAndPaginatedAuditsReturns200() {
any(LocalDate.class),
any(LocalDate.class),
isNull(),
+ isNull(),
isNull()
)).thenReturn(auditPage);
@@ -66,6 +70,7 @@ void getFilteredAndPaginatedAuditsReturns200() {
PAGE_SIZE,
null,
null,
+ null,
LocalDate.now().minusDays(1),
LocalDate.now()
);
@@ -85,16 +90,34 @@ void getFilteredAndPaginatedAuditsThrowsInvalidDateRangeExceptionForInvalidDateR
PAGE_SIZE,
null,
null,
+ null,
fromDate,
toDate
)
);
}
+ @Test
+ void getFilteredAndPaginatedAuditsThrowsInvalidParameterCombinationForBothSubjectIds() {
+ assertThrows(
+ InvalidParameterCombinationException.class, () ->
+ auditController.getFilteredAndPaginatedAudits(
+ PAGE_NUMBER,
+ PAGE_SIZE,
+ COURT_ID.toString(),
+ SERVICE_CENTRE_ID.toString(),
+ null,
+ LocalDate.now().minusDays(1),
+ LocalDate.now()
+ )
+ );
+ }
+
private Audit createAudit() {
return Audit.builder()
.id(AUDIT_ID)
- .courtId(COURT_ID)
+ .subjectId(COURT_ID)
+ .subjectType(AuditSubjectType.COURT)
.actionEntity(Court.class.getSimpleName())
.actionDataDiff(List.of(
new Change("isOpen", Boolean.FALSE, Boolean.TRUE),
diff --git a/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/AuditServiceTest.java b/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/AuditServiceTest.java
index f2e23344b..05f94eef3 100644
--- a/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/AuditServiceTest.java
+++ b/src/test/java/uk/gov/hmcts/reform/fact/data/api/services/AuditServiceTest.java
@@ -12,7 +12,9 @@
import uk.gov.hmcts.reform.fact.data.api.config.properties.AuditConfigurationProperties;
import uk.gov.hmcts.reform.fact.data.api.entities.Audit;
import uk.gov.hmcts.reform.fact.data.api.entities.Court;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.AuditSubjectType;
import uk.gov.hmcts.reform.fact.data.api.entities.types.Change;
+import uk.gov.hmcts.reform.fact.data.api.errorhandling.exceptions.InvalidParameterCombinationException;
import uk.gov.hmcts.reform.fact.data.api.repositories.AuditRepository;
import java.time.LocalDate;
@@ -35,6 +37,7 @@ class AuditServiceTest {
private static final UUID AUDIT_ID = UUID.randomUUID();
private static final UUID COURT_ID = UUID.randomUUID();
+ private static final UUID SERVICE_CENTRE_ID = UUID.randomUUID();
private static final UUID USER_ID = UUID.randomUUID();
private static final String EMAIL = "test.user@example.com";
@@ -79,6 +82,7 @@ void getFilteredAndPaginatedAuditsShouldCallFindByCreatedAtAfter() {
fromDate,
null,
null,
+ null,
null
);
@@ -105,6 +109,7 @@ void getFilteredAndPaginatedAuditsShouldCallFindByCreatedAtBetween() {
fromDate,
toDate,
null,
+ null,
null
);
@@ -117,8 +122,9 @@ void getFilteredAndPaginatedAuditsShouldCallFindByCreatedAtBetween() {
@Test
void getFilteredAndPaginatedAuditsShouldCallFindByCourtIdAndCreatedAtAfter() {
- when(auditRepository.findByCourtIdAndCreatedAtAfter(
+ when(auditRepository.findBySubjectIdAndSubjectTypeAndCreatedAtAfter(
eq(COURT_ID),
+ eq(AuditSubjectType.COURT),
eq(fromDateTime),
any(Pageable.class)
)
@@ -131,20 +137,22 @@ void getFilteredAndPaginatedAuditsShouldCallFindByCourtIdAndCreatedAtAfter() {
fromDate,
null,
COURT_ID.toString(),
+ null,
null
);
assertThat(result.getContent()).hasSize(1);
- verify(auditRepository).findByCourtIdAndCreatedAtAfter(
- eq(COURT_ID), eq(fromDateTime), any(Pageable.class)
+ verify(auditRepository).findBySubjectIdAndSubjectTypeAndCreatedAtAfter(
+ eq(COURT_ID), eq(AuditSubjectType.COURT), eq(fromDateTime), any(Pageable.class)
);
}
@Test
void getFilteredAndPaginatedAuditsShouldCallFindByCourtIdAndCreatedAtBetween() {
- when(auditRepository.findByCourtIdAndCreatedAtBetween(
+ when(auditRepository.findBySubjectIdAndSubjectTypeAndCreatedAtBetween(
eq(COURT_ID),
+ eq(AuditSubjectType.COURT),
eq(fromDateTime),
eq(toDateTime),
any(Pageable.class)
@@ -158,12 +166,13 @@ void getFilteredAndPaginatedAuditsShouldCallFindByCourtIdAndCreatedAtBetween() {
fromDate,
toDate,
COURT_ID.toString(),
+ null,
null
);
assertThat(result.getContent()).hasSize(1);
- verify(auditRepository).findByCourtIdAndCreatedAtBetween(
- eq(COURT_ID), eq(fromDateTime), eq(toDateTime), any(Pageable.class)
+ verify(auditRepository).findBySubjectIdAndSubjectTypeAndCreatedAtBetween(
+ eq(COURT_ID), eq(AuditSubjectType.COURT), eq(fromDateTime), eq(toDateTime), any(Pageable.class)
);
}
@@ -186,6 +195,7 @@ void getFilteredAndPaginatedAuditsShouldCallFindByCreatedAtAfterAndEmailAddressL
fromDate,
null,
null,
+ null,
EMAIL
);
@@ -213,6 +223,7 @@ void getFilteredAndPaginatedAuditsShouldCallFindByCreatedAtBetweenAndEmailAddres
fromDate,
toDate,
null,
+ null,
EMAIL
);
@@ -225,8 +236,9 @@ void getFilteredAndPaginatedAuditsShouldCallFindByCreatedAtBetweenAndEmailAddres
@Test
void getFilteredAndPaginatedAuditsShouldCallFindByCourtIdAndCreatedAtAfterAndEmailAddressLike() {
- when(auditRepository.findByCourtIdAndCreatedAtAfterAndEmailAddressLike(
+ when(auditRepository.findBySubjectIdAndSubjectTypeAndCreatedAtAfterAndEmailAddressLike(
eq(COURT_ID),
+ eq(AuditSubjectType.COURT),
eq(fromDateTime),
eq(EMAIL),
any(Pageable.class)
@@ -240,20 +252,22 @@ void getFilteredAndPaginatedAuditsShouldCallFindByCourtIdAndCreatedAtAfterAndEma
fromDate,
null,
COURT_ID.toString(),
+ null,
EMAIL
);
assertThat(result.getContent()).hasSize(1);
- verify(auditRepository).findByCourtIdAndCreatedAtAfterAndEmailAddressLike(
- eq(COURT_ID), eq(fromDateTime), eq(EMAIL), any(Pageable.class)
+ verify(auditRepository).findBySubjectIdAndSubjectTypeAndCreatedAtAfterAndEmailAddressLike(
+ eq(COURT_ID), eq(AuditSubjectType.COURT), eq(fromDateTime), eq(EMAIL), any(Pageable.class)
);
}
@Test
void getFilteredAndPaginatedAuditsShouldCallFindByCourtIdAndCreatedAtBetweenAndEmailAddressLike() {
- when(auditRepository.findByCourtIdAndCreatedAtBetweenAndEmailAddressLike(
+ when(auditRepository.findBySubjectIdAndSubjectTypeAndCreatedAtBetweenAndEmailAddressLike(
eq(COURT_ID),
+ eq(AuditSubjectType.COURT),
eq(fromDateTime),
eq(toDateTime),
eq(EMAIL),
@@ -268,12 +282,13 @@ void getFilteredAndPaginatedAuditsShouldCallFindByCourtIdAndCreatedAtBetweenAndE
fromDate,
toDate,
COURT_ID.toString(),
+ null,
EMAIL
);
assertThat(result.getContent()).hasSize(1);
- verify(auditRepository).findByCourtIdAndCreatedAtBetweenAndEmailAddressLike(
- eq(COURT_ID), eq(fromDateTime), eq(toDateTime), eq(EMAIL), any(Pageable.class)
+ verify(auditRepository).findBySubjectIdAndSubjectTypeAndCreatedAtBetweenAndEmailAddressLike(
+ eq(COURT_ID), eq(AuditSubjectType.COURT), eq(fromDateTime), eq(toDateTime), eq(EMAIL), any(Pageable.class)
);
}
@@ -292,11 +307,54 @@ void removeExpiredAuditEntriesShouldUserPropertiesAndCallDeleteAllBeforeCreatedD
// edge case tests
+ @Test
+ void getFilteredAndPaginatedAuditsShouldCallFindByServiceCentreIdAndCreatedAtAfter() {
+
+ when(auditRepository.findBySubjectIdAndSubjectTypeAndCreatedAtAfter(
+ eq(SERVICE_CENTRE_ID),
+ eq(AuditSubjectType.SERVICE_CENTRE),
+ eq(fromDateTime),
+ any(Pageable.class)
+ )
+ ).thenReturn(new PageImpl<>(List.of(createAudit())));
+
+ Page result =
+ auditService.getFilteredAndPaginatedAudits(
+ PAGE_NUMBER,
+ PAGE_SIZE,
+ fromDate,
+ null,
+ null,
+ SERVICE_CENTRE_ID.toString(),
+ null
+ );
+
+ assertThat(result.getContent()).hasSize(1);
+ verify(auditRepository).findBySubjectIdAndSubjectTypeAndCreatedAtAfter(
+ eq(SERVICE_CENTRE_ID), eq(AuditSubjectType.SERVICE_CENTRE), eq(fromDateTime), any(Pageable.class)
+ );
+ }
+
+ @Test
+ void shouldThrowInvalidParameterCombinationExceptionWhenBothSubjectIdsProvided() {
+ assertThrows(
+ InvalidParameterCombinationException.class, () -> auditService.getFilteredAndPaginatedAudits(
+ PAGE_NUMBER,
+ PAGE_SIZE,
+ fromDate,
+ null,
+ COURT_ID.toString(),
+ SERVICE_CENTRE_ID.toString(),
+ null
+ )
+ );
+ }
+
@Test
void shouldThrowNullPointerExceptionWhenFromDateIsNull() {
assertThrows(
NullPointerException.class, () -> {
- auditService.getFilteredAndPaginatedAudits(0, 1, null, null, null, null);
+ auditService.getFilteredAndPaginatedAudits(0, 1, null, null, null, null, null);
}
);
}
@@ -317,6 +375,7 @@ void shouldCallSimpleQueryForBlankEmail() {
fromDate,
null,
null,
+ null,
""
);
assertThat(result.getContent()).hasSize(1);
@@ -328,6 +387,7 @@ void shouldCallSimpleQueryForBlankEmail() {
fromDate,
null,
null,
+ null,
" "
);
assertThat(result.getContent()).hasSize(1);
@@ -340,7 +400,8 @@ void shouldCallSimpleQueryForBlankEmail() {
private Audit createAudit() {
return Audit.builder()
.id(AUDIT_ID)
- .courtId(COURT_ID)
+ .subjectId(COURT_ID)
+ .subjectType(AuditSubjectType.COURT)
.actionEntity(Court.class.getSimpleName())
.actionDataDiff(List.of(
new Change("isOpen", Boolean.FALSE, Boolean.TRUE),
From e750efbd6b5a32023e64cdce5c31c59c087b0fae Mon Sep 17 00:00:00 2001
From: Josh <23316525+josh-uk@users.noreply.github.com>
Date: Thu, 18 Jun 2026 17:28:10 +0100
Subject: [PATCH 03/40] FACT-2618 Added new controllers/services for service
centres (#275)
* Added new controllers/services for service centres
* Func test fixsxes
* Clean up and splitting
---
.../controllers/AuthFunctionalTest.java | 149 +++++++++++++++++
...CentreAddressControllerFunctionalTest.java | 68 ++++++++
...treAreasOfLawControllerFunctionalTest.java | 52 ++++++
...ontactDetailsControllerFunctionalTest.java | 75 +++++++++
...ServiceCentreControllerFunctionalTest.java | 69 ++++++++
.../functional/helpers/TestDataHelper.java | 64 +++++++
.../ServiceCentreAddressControllerTest.java | 142 ++++++++++++++++
...ServiceCentreAreasOfLawControllerTest.java | 99 +++++++++++
...iceCentreContactDetailsControllerTest.java | 144 ++++++++++++++++
.../ServiceCentreControllerTest.java | 130 +++++++++++++++
.../ServiceCentreAddressController.java | 149 +++++++++++++++++
.../ServiceCentreAreasOfLawController.java | 77 +++++++++
...ServiceCentreContactDetailsController.java | 155 +++++++++++++++++
.../controllers/ServiceCentreController.java | 109 ++++++++++++
.../controllers/TestingSupportController.java | 46 +++++
.../api/entities/ServiceCentreAreasOfLaw.java | 11 ++
.../ServiceCentreAddressRepository.java | 11 +-
.../ServiceCentreAreasOfLawRepository.java | 6 +-
...ServiceCentreContactDetailsRepository.java | 13 +-
.../repositories/ServiceCentreRepository.java | 11 +-
.../services/ServiceCentreAddressService.java | 127 ++++++++++++++
.../ServiceCentreAreasOfLawService.java | 83 +++++++++
.../ServiceCentreContactDetailsService.java | 137 +++++++++++++++
.../api/services/ServiceCentreService.java | 142 ++++++++++++++++
.../api/services/TestingSupportService.java | 134 ++++++++++++++-
.../V1.30__refresh_service_area_mappings.sql | 43 +++++
.../ServiceCentreAddressControllerTest.java | 93 +++++++++++
...ServiceCentreAreasOfLawControllerTest.java | 59 +++++++
...iceCentreContactDetailsControllerTest.java | 98 +++++++++++
.../ServiceCentreControllerTest.java | 84 ++++++++++
.../TestingSupportControllerTest.java | 42 +++++
.../ServiceCentreAddressServiceTest.java | 143 ++++++++++++++++
.../ServiceCentreAreasOfLawServiceTest.java | 125 ++++++++++++++
...erviceCentreContactDetailsServiceTest.java | 157 ++++++++++++++++++
.../services/ServiceCentreServiceTest.java | 141 ++++++++++++++++
.../services/TestingSupportServiceTest.java | 78 +++++++++
36 files changed, 3261 insertions(+), 5 deletions(-)
create mode 100644 src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/ServiceCentreAddressControllerFunctionalTest.java
create mode 100644 src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/ServiceCentreAreasOfLawControllerFunctionalTest.java
create mode 100644 src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/ServiceCentreContactDetailsControllerFunctionalTest.java
create mode 100644 src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/ServiceCentreControllerFunctionalTest.java
create mode 100644 src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAddressControllerTest.java
create mode 100644 src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAreasOfLawControllerTest.java
create mode 100644 src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreContactDetailsControllerTest.java
create mode 100644 src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreControllerTest.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAddressController.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAreasOfLawController.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreContactDetailsController.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreController.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/services/ServiceCentreAddressService.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/services/ServiceCentreAreasOfLawService.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/services/ServiceCentreContactDetailsService.java
create mode 100644 src/main/java/uk/gov/hmcts/reform/fact/data/api/services/ServiceCentreService.java
create mode 100644 src/main/resources/db/migration/V1.30__refresh_service_area_mappings.sql
create mode 100644 src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAddressControllerTest.java
create mode 100644 src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAreasOfLawControllerTest.java
create mode 100644 src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreContactDetailsControllerTest.java
create mode 100644 src/test/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreControllerTest.java
create mode 100644 src/test/java/uk/gov/hmcts/reform/fact/data/api/services/ServiceCentreAddressServiceTest.java
create mode 100644 src/test/java/uk/gov/hmcts/reform/fact/data/api/services/ServiceCentreAreasOfLawServiceTest.java
create mode 100644 src/test/java/uk/gov/hmcts/reform/fact/data/api/services/ServiceCentreContactDetailsServiceTest.java
create mode 100644 src/test/java/uk/gov/hmcts/reform/fact/data/api/services/ServiceCentreServiceTest.java
diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/AuthFunctionalTest.java b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/AuthFunctionalTest.java
index 4373eebf7..ea4959ee4 100644
--- a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/AuthFunctionalTest.java
+++ b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/AuthFunctionalTest.java
@@ -19,6 +19,10 @@
import uk.gov.hmcts.reform.fact.data.api.entities.CourtFacilities;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtOpeningHours;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtTranslation;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentre;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentreAddress;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentreAreasOfLaw;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentreContactDetails;
import uk.gov.hmcts.reform.fact.data.api.entities.User;
import uk.gov.hmcts.reform.fact.data.api.entities.types.AddressType;
import uk.gov.hmcts.reform.fact.data.api.entities.types.DayOfTheWeek;
@@ -82,6 +86,10 @@ private static UUID createCourtAsAdmin(String name) {
return UUID.fromString(createResponse.jsonPath().getString("id"));
}
+ private static UUID createServiceCentreAsAdmin(String name) {
+ return TestDataHelper.createServiceCentre(http, name, adminToken);
+ }
+
private static void assertViewerAllowed(Response adminResponse, Response viewerResponse, String endpoint) {
assertThat(adminResponse.statusCode())
.as("Admin status should not be 401 for %s", endpoint)
@@ -196,6 +204,140 @@ void courtControllerAuth() {
assertUnauthenticated(http.doPost("/courts/v1", testingCourt, ""), "/courts/v1 [POST]");
}
+ @Test
+ @DisplayName("Service centre controller endpoints enforce admin-only writes")
+ void serviceCentreControllerAuth() {
+ ServiceCentre serviceCentre = TestDataHelper.buildServiceCentre(
+ http,
+ "Auth Test Service Centre",
+ adminToken
+ );
+
+ Response createViewer = http.doPost("/service-centres/v1", serviceCentre, viewerToken);
+ Response createAdmin = http.doPost("/service-centres/v1", serviceCentre, adminToken);
+ assertViewerForbidden(createViewer, "/service-centres/v1 [POST]");
+ assertThat(createAdmin.statusCode()).isEqualTo(201);
+
+ ServiceCentre createdServiceCentre = createAdmin.as(ServiceCentre.class);
+ UUID serviceCentreId = createdServiceCentre.getId();
+ assertThat(createdServiceCentre.getCatchmentType().name()).isEqualTo("NATIONAL");
+
+ String[] readEndpoints = new String[]{
+ "/service-centres/" + serviceCentreId + "/v1"
+ };
+
+ for (String endpoint : readEndpoints) {
+ Response admin = http.doGet(endpoint, adminToken);
+ Response viewer = http.doGet(endpoint, viewerToken);
+ assertViewerAllowed(admin, viewer, endpoint);
+ }
+
+ serviceCentre.setName(TestDataHelper.appendRandomSuffixToCourtName("Auth Test Service Centre Updated"));
+ Response updateViewer = http.doPut("/service-centres/" + serviceCentreId + "/v1", serviceCentre, viewerToken);
+ Response updateAdmin = http.doPut("/service-centres/" + serviceCentreId + "/v1", serviceCentre, adminToken);
+ assertViewerForbidden(updateViewer, "/service-centres/{serviceCentreId}/v1 [PUT]");
+ assertThat(updateAdmin.statusCode()).isEqualTo(200);
+
+ assertUnauthenticated(
+ http.doGet("/service-centres/" + serviceCentreId + "/v1", ""),
+ "/service-centres/{serviceCentreId}/v1 [GET]"
+ );
+ assertUnauthenticated(http.doPost("/service-centres/v1", serviceCentre, ""), "/service-centres/v1 [POST]");
+ }
+
+ @Test
+ @DisplayName("Service centre child endpoints enforce admin-only writes")
+ void serviceCentreChildControllerAuth() {
+ UUID serviceCentreId = createServiceCentreAsAdmin("Auth Test Service Centre Children");
+ ServiceCentreAddress address = ServiceCentreAddress.builder()
+ .serviceCentreId(serviceCentreId)
+ .addressLine1("1 Auth Street")
+ .townCity("London")
+ .postcode("NW10 4DX")
+ .addressType(AddressType.VISIT_OR_CONTACT_US)
+ .build();
+
+ Response createAddressAdmin =
+ http.doPost("/service-centres/" + serviceCentreId + "/v1/address", address, adminToken);
+ assertThat(createAddressAdmin.statusCode()).isEqualTo(201);
+ UUID addressId = UUID.fromString(createAddressAdmin.jsonPath().getString("id"));
+
+ assertViewerAllowed(
+ http.doGet("/service-centres/" + serviceCentreId + "/v1/address", adminToken),
+ http.doGet("/service-centres/" + serviceCentreId + "/v1/address", viewerToken),
+ "/service-centres/{serviceCentreId}/v1/address [GET]"
+ );
+ assertViewerForbidden(
+ http.doPost("/service-centres/" + serviceCentreId + "/v1/address", address, viewerToken),
+ "/service-centres/{serviceCentreId}/v1/address [POST]"
+ );
+ assertViewerForbidden(
+ http.doPut("/service-centres/" + serviceCentreId + "/v1/address/" + addressId, address, viewerToken),
+ "/service-centres/{serviceCentreId}/v1/address/{addressId} [PUT]"
+ );
+
+ UUID contactTypeId = UUID.fromString(http.doGet("/types/v1/contact-description-types", adminToken)
+ .jsonPath().getString("[0].id"));
+ ServiceCentreContactDetails contactDetails = ServiceCentreContactDetails.builder()
+ .serviceCentreId(serviceCentreId)
+ .serviceCentreContactDescriptionId(contactTypeId)
+ .explanation("Auth test")
+ .email("servicecentre-auth@test.gov.uk")
+ .phoneNumber("01234567890")
+ .build();
+ Response createContactAdmin = http.doPost(
+ "/service-centres/" + serviceCentreId + "/v1/contact-details",
+ contactDetails,
+ adminToken
+ );
+ assertThat(createContactAdmin.statusCode()).isEqualTo(201);
+ UUID contactId = UUID.fromString(createContactAdmin.jsonPath().getString("id"));
+
+ assertViewerAllowed(
+ http.doGet("/service-centres/" + serviceCentreId + "/v1/contact-details", adminToken),
+ http.doGet("/service-centres/" + serviceCentreId + "/v1/contact-details", viewerToken),
+ "/service-centres/{serviceCentreId}/v1/contact-details [GET]"
+ );
+ assertViewerForbidden(
+ http.doPut(
+ "/service-centres/" + serviceCentreId + "/v1/contact-details/" + contactId,
+ contactDetails,
+ viewerToken
+ ),
+ "/service-centres/{serviceCentreId}/v1/contact-details/{contactId} [PUT]"
+ );
+
+ UUID areaOfLawId = UUID.fromString(http.doGet("/types/v1/areas-of-law", adminToken)
+ .jsonPath().getString("[0].id"));
+ ServiceCentreAreasOfLaw areasOfLaw = ServiceCentreAreasOfLaw.builder()
+ .serviceCentreId(serviceCentreId)
+ .areasOfLaw(List.of(areaOfLawId))
+ .build();
+ assertThat(http.doPut(
+ "/service-centres/" + serviceCentreId + "/v1/areas-of-law",
+ areasOfLaw,
+ adminToken
+ ).statusCode()).isEqualTo(201);
+ assertViewerAllowed(
+ http.doGet("/service-centres/" + serviceCentreId + "/v1/areas-of-law", adminToken),
+ http.doGet("/service-centres/" + serviceCentreId + "/v1/areas-of-law", viewerToken),
+ "/service-centres/{serviceCentreId}/v1/areas-of-law [GET]"
+ );
+ assertViewerForbidden(
+ http.doPut("/service-centres/" + serviceCentreId + "/v1/areas-of-law", areasOfLaw, viewerToken),
+ "/service-centres/{serviceCentreId}/v1/areas-of-law [PUT]"
+ );
+
+ assertViewerForbidden(
+ http.doDelete("/service-centres/" + serviceCentreId + "/v1/address/" + addressId, viewerToken),
+ "/service-centres/{serviceCentreId}/v1/address/{addressId} [DELETE]"
+ );
+ assertViewerForbidden(
+ http.doDelete("/service-centres/" + serviceCentreId + "/v1/contact-details/" + contactId, viewerToken),
+ "/service-centres/{serviceCentreId}/v1/contact-details/{contactId} [DELETE]"
+ );
+ }
+
@Test
@DisplayName("Court address endpoints enforce admin-only writes")
void courtAddressAuth() {
@@ -657,11 +799,18 @@ void testingSupportEndpointAuth() {
endpoint = "/testing-support/courts/name-prefix/Auth Test Cleanup";
noToken = http.doDelete(endpoint, "");
assertOpen(noToken, endpoint);
+ endpoint = "/testing-support/service-centres?serviceCentreName=Auth Test Service Centre Support";
+ noToken = http.doGet(endpoint);
+ assertOpen(noToken, endpoint);
+ endpoint = "/testing-support/service-centres/name-prefix/Auth Test Service Centre Support";
+ noToken = http.doDelete(endpoint, "");
+ assertOpen(noToken, endpoint);
}
@AfterAll
static void cleanUp() {
http.doDelete("/testing-support/courts/name-prefix/Test Court", adminToken);
http.doDelete("/testing-support/courts/name-prefix/Auth Test", adminToken);
+ http.doDelete("/testing-support/service-centres/name-prefix/Auth Test", adminToken);
}
}
diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/ServiceCentreAddressControllerFunctionalTest.java b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/ServiceCentreAddressControllerFunctionalTest.java
new file mode 100644
index 000000000..a3f11d344
--- /dev/null
+++ b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/ServiceCentreAddressControllerFunctionalTest.java
@@ -0,0 +1,68 @@
+package uk.gov.hmcts.reform.fact.functional.controllers;
+
+import io.qameta.allure.Feature;
+import io.restassured.response.Response;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentreAddress;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.AddressType;
+import uk.gov.hmcts.reform.fact.functional.helpers.AssertionHelper;
+import uk.gov.hmcts.reform.fact.functional.helpers.TestDataHelper;
+import uk.gov.hmcts.reform.fact.functional.http.HttpClient;
+
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.http.HttpStatus.CREATED;
+import static org.springframework.http.HttpStatus.NO_CONTENT;
+import static org.springframework.http.HttpStatus.OK;
+
+@Feature("Service Centre Address Controller")
+@DisplayName("Service Centre Address Controller")
+public final class ServiceCentreAddressControllerFunctionalTest {
+
+ private static final HttpClient http = new HttpClient();
+ private static final String TEST_PREFIX = "Test Service Centre Address";
+
+ @Test
+ @DisplayName("Service centre address endpoints support CRUD")
+ void shouldCreateReadUpdateAndDeleteServiceCentreAddress() {
+ UUID serviceCentreId = TestDataHelper.createServiceCentre(http, TEST_PREFIX);
+ ServiceCentreAddress address = ServiceCentreAddress.builder()
+ .serviceCentreId(serviceCentreId)
+ .addressLine1("1 Test Street")
+ .townCity("London")
+ .postcode("SW1A 1AA")
+ .addressType(AddressType.VISIT_OR_CONTACT_US)
+ .build();
+
+ Response createResponse = http.doPost("/service-centres/" + serviceCentreId + "/v1/address", address);
+ AssertionHelper.assertStatus(createResponse, CREATED);
+ UUID addressId = UUID.fromString(createResponse.jsonPath().getString("id"));
+
+ AssertionHelper.assertStatus(http.doGet("/service-centres/" + serviceCentreId + "/v1/address"), OK);
+
+ Response getResponse = http.doGet("/service-centres/" + serviceCentreId + "/v1/address/" + addressId);
+ AssertionHelper.assertStatus(getResponse, OK);
+ assertThat(getResponse.jsonPath().getString("addressLine1")).isEqualTo("1 Test Street");
+
+ address.setAddressLine1("2 Updated Street");
+ Response updateResponse = http.doPut(
+ "/service-centres/" + serviceCentreId + "/v1/address/" + addressId,
+ address
+ );
+ AssertionHelper.assertStatus(updateResponse, OK);
+ assertThat(updateResponse.jsonPath().getString("addressLine1")).isEqualTo("2 Updated Street");
+
+ AssertionHelper.assertStatus(
+ http.doDelete("/service-centres/" + serviceCentreId + "/v1/address/" + addressId),
+ NO_CONTENT
+ );
+ }
+
+ @AfterAll
+ static void cleanUpTestData() {
+ http.doDelete("/testing-support/service-centres/name-prefix/" + TEST_PREFIX);
+ }
+}
diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/ServiceCentreAreasOfLawControllerFunctionalTest.java b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/ServiceCentreAreasOfLawControllerFunctionalTest.java
new file mode 100644
index 000000000..a0f1c6b48
--- /dev/null
+++ b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/ServiceCentreAreasOfLawControllerFunctionalTest.java
@@ -0,0 +1,52 @@
+package uk.gov.hmcts.reform.fact.functional.controllers;
+
+import io.qameta.allure.Feature;
+import io.restassured.response.Response;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentreAreasOfLaw;
+import uk.gov.hmcts.reform.fact.functional.helpers.AssertionHelper;
+import uk.gov.hmcts.reform.fact.functional.helpers.TestDataHelper;
+import uk.gov.hmcts.reform.fact.functional.http.HttpClient;
+
+import java.util.List;
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.http.HttpStatus.CREATED;
+import static org.springframework.http.HttpStatus.OK;
+
+@Feature("Service Centre Areas Of Law Controller")
+@DisplayName("Service Centre Areas Of Law Controller")
+public final class ServiceCentreAreasOfLawControllerFunctionalTest {
+
+ private static final HttpClient http = new HttpClient();
+ private static final String TEST_PREFIX = "Test Service Centre Areas Of Law";
+
+ @Test
+ @DisplayName("Service centre areas of law endpoints support get and update")
+ void shouldSetAndGetServiceCentreAreasOfLaw() {
+ UUID serviceCentreId = TestDataHelper.createServiceCentre(http, TEST_PREFIX);
+ UUID areaOfLawId = UUID.fromString(http.doGet("/types/v1/areas-of-law").jsonPath().getString("[0].id"));
+ ServiceCentreAreasOfLaw areasOfLaw = ServiceCentreAreasOfLaw.builder()
+ .serviceCentreId(serviceCentreId)
+ .areasOfLaw(List.of(areaOfLawId))
+ .build();
+
+ Response updateResponse = http.doPut(
+ "/service-centres/" + serviceCentreId + "/v1/areas-of-law",
+ areasOfLaw
+ );
+ AssertionHelper.assertStatus(updateResponse, CREATED);
+
+ Response getResponse = http.doGet("/service-centres/" + serviceCentreId + "/v1/areas-of-law");
+ AssertionHelper.assertStatus(getResponse, OK);
+ assertThat(getResponse.jsonPath().getMap("$")).isNotEmpty();
+ }
+
+ @AfterAll
+ static void cleanUpTestData() {
+ http.doDelete("/testing-support/service-centres/name-prefix/" + TEST_PREFIX);
+ }
+}
diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/ServiceCentreContactDetailsControllerFunctionalTest.java b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/ServiceCentreContactDetailsControllerFunctionalTest.java
new file mode 100644
index 000000000..8ae1b0acf
--- /dev/null
+++ b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/ServiceCentreContactDetailsControllerFunctionalTest.java
@@ -0,0 +1,75 @@
+package uk.gov.hmcts.reform.fact.functional.controllers;
+
+import io.qameta.allure.Feature;
+import io.restassured.response.Response;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentreContactDetails;
+import uk.gov.hmcts.reform.fact.functional.helpers.AssertionHelper;
+import uk.gov.hmcts.reform.fact.functional.helpers.TestDataHelper;
+import uk.gov.hmcts.reform.fact.functional.http.HttpClient;
+
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.http.HttpStatus.CREATED;
+import static org.springframework.http.HttpStatus.NO_CONTENT;
+import static org.springframework.http.HttpStatus.OK;
+
+@Feature("Service Centre Contact Details Controller")
+@DisplayName("Service Centre Contact Details Controller")
+public final class ServiceCentreContactDetailsControllerFunctionalTest {
+
+ private static final HttpClient http = new HttpClient();
+ private static final String TEST_PREFIX = "Test Service Centre Contact";
+
+ @Test
+ @DisplayName("Service centre contact details endpoints support CRUD")
+ void shouldCreateReadUpdateAndDeleteServiceCentreContactDetails() {
+ UUID serviceCentreId = TestDataHelper.createServiceCentre(http, TEST_PREFIX);
+ UUID contactDescriptionId = UUID.fromString(
+ http.doGet("/types/v1/contact-description-types").jsonPath().getString("[0].id")
+ );
+ ServiceCentreContactDetails contactDetails = ServiceCentreContactDetails.builder()
+ .serviceCentreId(serviceCentreId)
+ .serviceCentreContactDescriptionId(contactDescriptionId)
+ .explanation("General enquiries")
+ .email("servicecentre@example.com")
+ .phoneNumber("01234567890")
+ .build();
+
+ Response createResponse = http.doPost(
+ "/service-centres/" + serviceCentreId + "/v1/contact-details",
+ contactDetails
+ );
+ AssertionHelper.assertStatus(createResponse, CREATED);
+ UUID contactId = UUID.fromString(createResponse.jsonPath().getString("id"));
+
+ AssertionHelper.assertStatus(http.doGet("/service-centres/" + serviceCentreId + "/v1/contact-details"), OK);
+
+ Response getResponse = http.doGet(
+ "/service-centres/" + serviceCentreId + "/v1/contact-details/" + contactId
+ );
+ AssertionHelper.assertStatus(getResponse, OK);
+ assertThat(getResponse.jsonPath().getString("email")).isEqualTo("servicecentre@example.com");
+
+ contactDetails.setEmail("updated-servicecentre@example.com");
+ Response updateResponse = http.doPut(
+ "/service-centres/" + serviceCentreId + "/v1/contact-details/" + contactId,
+ contactDetails
+ );
+ AssertionHelper.assertStatus(updateResponse, OK);
+ assertThat(updateResponse.jsonPath().getString("email")).isEqualTo("updated-servicecentre@example.com");
+
+ AssertionHelper.assertStatus(
+ http.doDelete("/service-centres/" + serviceCentreId + "/v1/contact-details/" + contactId),
+ NO_CONTENT
+ );
+ }
+
+ @AfterAll
+ static void cleanUpTestData() {
+ http.doDelete("/testing-support/service-centres/name-prefix/" + TEST_PREFIX);
+ }
+}
diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/ServiceCentreControllerFunctionalTest.java b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/ServiceCentreControllerFunctionalTest.java
new file mode 100644
index 000000000..83844d435
--- /dev/null
+++ b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/controllers/ServiceCentreControllerFunctionalTest.java
@@ -0,0 +1,69 @@
+package uk.gov.hmcts.reform.fact.functional.controllers;
+
+import io.qameta.allure.Feature;
+import io.restassured.response.Response;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentre;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.CatchmentType;
+import uk.gov.hmcts.reform.fact.functional.helpers.AssertionHelper;
+import uk.gov.hmcts.reform.fact.functional.helpers.TestDataHelper;
+import uk.gov.hmcts.reform.fact.functional.http.HttpClient;
+
+import java.util.Map;
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.http.HttpStatus.CREATED;
+import static org.springframework.http.HttpStatus.NOT_FOUND;
+import static org.springframework.http.HttpStatus.OK;
+
+@Feature("Service Centre Controller")
+@DisplayName("Service Centre Controller")
+public final class ServiceCentreControllerFunctionalTest {
+
+ private static final HttpClient http = new HttpClient();
+ private static final String TEST_PREFIX = "Test Service Centre";
+
+ @Test
+ @DisplayName("Service centre top-level endpoints create, read, update and name")
+ void shouldCreateReadAndUpdateServiceCentre() {
+ ServiceCentre serviceCentre = TestDataHelper.buildServiceCentre(http, TEST_PREFIX + " Top Level");
+
+ Response createResponse = http.doPost("/service-centres/v1", serviceCentre);
+ AssertionHelper.assertStatus(createResponse, CREATED);
+
+ UUID serviceCentreId = UUID.fromString(createResponse.jsonPath().getString("id"));
+ assertThat(createResponse.jsonPath().getBoolean("open")).isFalse();
+ assertThat(createResponse.jsonPath().getString("catchmentType")).isEqualTo(CatchmentType.NATIONAL.name());
+
+ Response getResponse = http.doGet("/service-centres/" + serviceCentreId + "/v1");
+ AssertionHelper.assertStatus(getResponse, OK);
+ String createdName = createResponse.jsonPath().getString("name");
+ assertThat(getResponse.jsonPath().getString("name")).isEqualTo(createdName);
+
+ Response nameResponse = http.doGet("/service-centres/name/v1", Map.of("name", createdName));
+ AssertionHelper.assertStatus(nameResponse, OK);
+ assertThat(UUID.fromString(nameResponse.jsonPath().getString("id"))).isEqualTo(serviceCentreId);
+
+ serviceCentre.setName(TestDataHelper.appendRandomSuffixToCourtName(TEST_PREFIX + " Updated"));
+ serviceCentre.setOpen(true);
+ serviceCentre.setWarningNotice("Updated warning notice");
+ serviceCentre.setCatchmentType(CatchmentType.NATIONAL);
+
+ Response updateResponse = http.doPut("/service-centres/" + serviceCentreId + "/v1", serviceCentre);
+ AssertionHelper.assertStatus(updateResponse, OK);
+ assertThat(updateResponse.jsonPath().getBoolean("open")).isTrue();
+ assertThat(updateResponse.jsonPath().getString("warningNotice")).isEqualTo("Updated warning notice");
+ assertThat(updateResponse.jsonPath().getString("catchmentType")).isEqualTo(CatchmentType.NATIONAL.name());
+
+ Response missingResponse = http.doGet("/service-centres/" + UUID.randomUUID() + "/v1");
+ AssertionHelper.assertStatus(missingResponse, NOT_FOUND);
+ }
+
+ @AfterAll
+ static void cleanUpTestData() {
+ http.doDelete("/testing-support/service-centres/name-prefix/" + TEST_PREFIX);
+ }
+}
diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/helpers/TestDataHelper.java b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/helpers/TestDataHelper.java
index b3814b765..cfc31a467 100644
--- a/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/helpers/TestDataHelper.java
+++ b/src/functionalTest/java/uk/gov/hmcts/reform/fact/functional/helpers/TestDataHelper.java
@@ -7,7 +7,9 @@
import uk.gov.hmcts.reform.fact.data.api.entities.Court;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtFacilities;
import uk.gov.hmcts.reform.fact.data.api.entities.CourtLock;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentre;
import uk.gov.hmcts.reform.fact.data.api.entities.User;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.CatchmentType;
import uk.gov.hmcts.reform.fact.data.api.entities.types.Page;
import uk.gov.hmcts.reform.fact.data.api.entities.types.UserRole;
import uk.gov.hmcts.reform.fact.data.api.os.OsData;
@@ -165,6 +167,68 @@ public static UUID createCourt(
return UUID.fromString(createResponse.jsonPath().getString("id"));
}
+ /**
+ * Builds a test service centre with the given name.
+ *
+ * @param http the HTTP client
+ * @param serviceCentreName the name for the test service centre
+ * @return the service centre body
+ */
+ public static ServiceCentre buildServiceCentre(final HttpClient http, final String serviceCentreName) {
+ return buildServiceCentre(http, serviceCentreName, HttpClient.getAdminBearerToken());
+ }
+
+ /**
+ * Builds a test service centre with the given name.
+ *
+ * @param http the HTTP client
+ * @param serviceCentreName the name for the test service centre
+ * @param bearerToken the bearer token to use when fetching reference data
+ * @return the service centre body
+ */
+ public static ServiceCentre buildServiceCentre(final HttpClient http,
+ final String serviceCentreName,
+ final String bearerToken) {
+ final ServiceCentre serviceCentre = new ServiceCentre();
+ serviceCentre.setName(appendRandomSuffixToCourtName(serviceCentreName));
+ serviceCentre.setOpen(false);
+ serviceCentre.setServiceAreaIds(List.of(UUID.fromString(
+ http.doGet("/types/v1/service-areas", bearerToken).jsonPath().getString("[0].id")
+ )));
+ serviceCentre.setCatchmentType(CatchmentType.NATIONAL);
+ return serviceCentre;
+ }
+
+ /**
+ * Creates a test service centre with the given name.
+ *
+ * @param http the HTTP client
+ * @param serviceCentreName the name for the test service centre
+ * @return the created service centre UUID
+ */
+ public static UUID createServiceCentre(final HttpClient http, final String serviceCentreName) {
+ return createServiceCentre(http, serviceCentreName, HttpClient.getAdminBearerToken());
+ }
+
+ /**
+ * Creates a test service centre with the given name.
+ *
+ * @param http the HTTP client
+ * @param serviceCentreName the name for the test service centre
+ * @param bearerToken the bearer token to use when creating the service centre
+ * @return the created service centre UUID
+ */
+ public static UUID createServiceCentre(final HttpClient http,
+ final String serviceCentreName,
+ final String bearerToken) {
+ final ServiceCentre serviceCentre = buildServiceCentre(http, serviceCentreName, bearerToken);
+
+ final Response createResponse = http.doPost("/service-centres/v1", serviceCentre, bearerToken);
+ assertThat(createResponse.statusCode()).isEqualTo(CREATED.value());
+
+ return UUID.fromString(createResponse.jsonPath().getString("id"));
+ }
+
/**
* Builds a CourtFacilities object with all fields set to true by default.
*
diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAddressControllerTest.java b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAddressControllerTest.java
new file mode 100644
index 000000000..1b3797dfc
--- /dev/null
+++ b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAddressControllerTest.java
@@ -0,0 +1,142 @@
+package uk.gov.hmcts.reform.fact.data.api.controllers;
+
+import io.qameta.allure.Feature;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
+import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.bean.override.mockito.MockitoBean;
+import org.springframework.test.web.servlet.MockMvc;
+import tools.jackson.databind.ObjectMapper;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentreAddress;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.AddressType;
+import uk.gov.hmcts.reform.fact.data.api.errorhandling.exceptions.NotFoundException;
+import uk.gov.hmcts.reform.fact.data.api.services.ServiceCentreAddressService;
+
+import java.util.List;
+import java.util.UUID;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@Feature("Service Centre Address Controller")
+@DisplayName("Service Centre Address Controller")
+@WebMvcTest(ServiceCentreAddressController.class)
+@AutoConfigureMockMvc(addFilters = false)
+@ActiveProfiles("test")
+class ServiceCentreAddressControllerTest {
+
+ private static final UUID SERVICE_CENTRE_ID = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
+ private static final UUID ADDRESS_ID = UUID.fromString("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @MockitoBean
+ private ServiceCentreAddressService serviceCentreAddressService;
+
+ @Test
+ void getAddressesReturnsAddresses() throws Exception {
+ ServiceCentreAddress address = buildAddress();
+ when(serviceCentreAddressService.getAddresses(SERVICE_CENTRE_ID)).thenReturn(List.of(address));
+
+ mockMvc.perform(get("/service-centres/{serviceCentreId}/v1/address", SERVICE_CENTRE_ID))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$[0].id").value(ADDRESS_ID.toString()));
+ }
+
+ @Test
+ void getAddressReturnsAddress() throws Exception {
+ ServiceCentreAddress address = buildAddress();
+ when(serviceCentreAddressService.getAddress(SERVICE_CENTRE_ID, ADDRESS_ID)).thenReturn(address);
+
+ mockMvc.perform(get("/service-centres/{serviceCentreId}/v1/address/{addressId}",
+ SERVICE_CENTRE_ID,
+ ADDRESS_ID))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(ADDRESS_ID.toString()));
+ }
+
+ @Test
+ void getAddressReturnsBadRequestForInvalidUuid() throws Exception {
+ mockMvc.perform(get("/service-centres/{serviceCentreId}/v1/address/{addressId}",
+ SERVICE_CENTRE_ID,
+ "invalid-uuid"))
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ void postAddressCreatesAddress() throws Exception {
+ ServiceCentreAddress address = buildAddress();
+ when(serviceCentreAddressService.createAddress(eq(SERVICE_CENTRE_ID), any(ServiceCentreAddress.class)))
+ .thenReturn(address);
+
+ mockMvc.perform(post("/service-centres/{serviceCentreId}/v1/address", SERVICE_CENTRE_ID)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(address)))
+ .andExpect(status().isCreated())
+ .andExpect(jsonPath("$.id").value(ADDRESS_ID.toString()));
+ }
+
+ @Test
+ void putAddressUpdatesAddress() throws Exception {
+ ServiceCentreAddress address = buildAddress();
+ when(serviceCentreAddressService.updateAddress(
+ eq(SERVICE_CENTRE_ID),
+ eq(ADDRESS_ID),
+ any(ServiceCentreAddress.class)
+ )).thenReturn(address);
+
+ mockMvc.perform(put("/service-centres/{serviceCentreId}/v1/address/{addressId}",
+ SERVICE_CENTRE_ID,
+ ADDRESS_ID)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(address)))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(ADDRESS_ID.toString()));
+ }
+
+ @Test
+ void deleteAddressReturnsNoContent() throws Exception {
+ mockMvc.perform(delete("/service-centres/{serviceCentreId}/v1/address/{addressId}",
+ SERVICE_CENTRE_ID,
+ ADDRESS_ID))
+ .andExpect(status().isNoContent());
+ }
+
+ @Test
+ void deleteAddressReturnsNotFound() throws Exception {
+ doThrow(new NotFoundException("Missing"))
+ .when(serviceCentreAddressService).deleteAddress(SERVICE_CENTRE_ID, ADDRESS_ID);
+
+ mockMvc.perform(delete("/service-centres/{serviceCentreId}/v1/address/{addressId}",
+ SERVICE_CENTRE_ID,
+ ADDRESS_ID))
+ .andExpect(status().isNotFound());
+ }
+
+ private ServiceCentreAddress buildAddress() {
+ return ServiceCentreAddress.builder()
+ .id(ADDRESS_ID)
+ .serviceCentreId(SERVICE_CENTRE_ID)
+ .addressLine1("1 Test Street")
+ .townCity("London")
+ .postcode("SW1A 1AA")
+ .addressType(AddressType.VISIT_OR_CONTACT_US)
+ .build();
+ }
+}
diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAreasOfLawControllerTest.java b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAreasOfLawControllerTest.java
new file mode 100644
index 000000000..bd7606622
--- /dev/null
+++ b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAreasOfLawControllerTest.java
@@ -0,0 +1,99 @@
+package uk.gov.hmcts.reform.fact.data.api.controllers;
+
+import io.qameta.allure.Feature;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
+import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.bean.override.mockito.MockitoBean;
+import org.springframework.test.web.servlet.MockMvc;
+import tools.jackson.databind.ObjectMapper;
+import uk.gov.hmcts.reform.fact.data.api.entities.AreaOfLawType;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentreAreasOfLaw;
+import uk.gov.hmcts.reform.fact.data.api.errorhandling.exceptions.NotFoundException;
+import uk.gov.hmcts.reform.fact.data.api.services.ServiceCentreAreasOfLawService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@Feature("Service Centre Areas Of Law Controller")
+@DisplayName("Service Centre Areas Of Law Controller")
+@WebMvcTest(ServiceCentreAreasOfLawController.class)
+@AutoConfigureMockMvc(addFilters = false)
+@ActiveProfiles("test")
+class ServiceCentreAreasOfLawControllerTest {
+
+ private static final UUID SERVICE_CENTRE_ID = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
+ private static final UUID AREA_OF_LAW_ID = UUID.fromString("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @MockitoBean
+ private ServiceCentreAreasOfLawService serviceCentreAreasOfLawService;
+
+ @Test
+ void getAreasOfLawReturnsMap() throws Exception {
+ AreaOfLawType areaOfLawType = AreaOfLawType.builder()
+ .id(AREA_OF_LAW_ID)
+ .name("Civil")
+ .displayName("Civil")
+ .build();
+
+ when(serviceCentreAreasOfLawService.getAreasOfLawStatusByServiceCentreId(SERVICE_CENTRE_ID))
+ .thenReturn(Map.of(areaOfLawType, true));
+
+ mockMvc.perform(get("/service-centres/{serviceCentreId}/v1/areas-of-law", SERVICE_CENTRE_ID))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.*").exists());
+ }
+
+ @Test
+ void getAreasOfLawReturnsBadRequestForInvalidUuid() throws Exception {
+ mockMvc.perform(get("/service-centres/{serviceCentreId}/v1/areas-of-law", "invalid-uuid"))
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ void getAreasOfLawReturnsNotFoundWhenMissing() throws Exception {
+ when(serviceCentreAreasOfLawService.getAreasOfLawStatusByServiceCentreId(SERVICE_CENTRE_ID))
+ .thenThrow(new NotFoundException("Missing"));
+
+ mockMvc.perform(get("/service-centres/{serviceCentreId}/v1/areas-of-law", SERVICE_CENTRE_ID))
+ .andExpect(status().isNotFound());
+ }
+
+ @Test
+ void putAreasOfLawReturnsCreated() throws Exception {
+ ServiceCentreAreasOfLaw areasOfLaw = ServiceCentreAreasOfLaw.builder()
+ .serviceCentreId(SERVICE_CENTRE_ID)
+ .areasOfLaw(List.of(AREA_OF_LAW_ID))
+ .build();
+
+ when(serviceCentreAreasOfLawService.setServiceCentreAreasOfLaw(
+ eq(SERVICE_CENTRE_ID),
+ any(ServiceCentreAreasOfLaw.class)
+ )).thenReturn(areasOfLaw);
+
+ mockMvc.perform(put("/service-centres/{serviceCentreId}/v1/areas-of-law", SERVICE_CENTRE_ID)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(areasOfLaw)))
+ .andExpect(status().isCreated())
+ .andExpect(jsonPath("$.serviceCentreId").value(SERVICE_CENTRE_ID.toString()));
+ }
+}
diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreContactDetailsControllerTest.java b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreContactDetailsControllerTest.java
new file mode 100644
index 000000000..2baa81b39
--- /dev/null
+++ b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreContactDetailsControllerTest.java
@@ -0,0 +1,144 @@
+package uk.gov.hmcts.reform.fact.data.api.controllers;
+
+import io.qameta.allure.Feature;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
+import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.bean.override.mockito.MockitoBean;
+import org.springframework.test.web.servlet.MockMvc;
+import tools.jackson.databind.ObjectMapper;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentreContactDetails;
+import uk.gov.hmcts.reform.fact.data.api.errorhandling.exceptions.NotFoundException;
+import uk.gov.hmcts.reform.fact.data.api.services.ServiceCentreContactDetailsService;
+
+import java.util.List;
+import java.util.UUID;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@Feature("Service Centre Contact Details Controller")
+@DisplayName("Service Centre Contact Details Controller")
+@WebMvcTest(ServiceCentreContactDetailsController.class)
+@AutoConfigureMockMvc(addFilters = false)
+@ActiveProfiles("test")
+class ServiceCentreContactDetailsControllerTest {
+
+ private static final UUID SERVICE_CENTRE_ID = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
+ private static final UUID CONTACT_ID = UUID.fromString("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @MockitoBean
+ private ServiceCentreContactDetailsService serviceCentreContactDetailsService;
+
+ @Test
+ void getContactDetailsReturnsContactDetails() throws Exception {
+ ServiceCentreContactDetails contactDetails = buildContactDetails();
+ when(serviceCentreContactDetailsService.getContactDetails(SERVICE_CENTRE_ID))
+ .thenReturn(List.of(contactDetails));
+
+ mockMvc.perform(get("/service-centres/{serviceCentreId}/v1/contact-details", SERVICE_CENTRE_ID))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$[0].id").value(CONTACT_ID.toString()));
+ }
+
+ @Test
+ void getContactDetailReturnsContactDetail() throws Exception {
+ ServiceCentreContactDetails contactDetails = buildContactDetails();
+ when(serviceCentreContactDetailsService.getContactDetail(SERVICE_CENTRE_ID, CONTACT_ID))
+ .thenReturn(contactDetails);
+
+ mockMvc.perform(get("/service-centres/{serviceCentreId}/v1/contact-details/{contactId}",
+ SERVICE_CENTRE_ID,
+ CONTACT_ID))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(CONTACT_ID.toString()));
+ }
+
+ @Test
+ void getContactDetailReturnsBadRequestForInvalidUuid() throws Exception {
+ mockMvc.perform(get("/service-centres/{serviceCentreId}/v1/contact-details/{contactId}",
+ SERVICE_CENTRE_ID,
+ "invalid-uuid"))
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ void postContactDetailCreatesContactDetail() throws Exception {
+ ServiceCentreContactDetails contactDetails = buildContactDetails();
+ when(serviceCentreContactDetailsService.createContactDetail(
+ eq(SERVICE_CENTRE_ID),
+ any(ServiceCentreContactDetails.class)
+ )).thenReturn(contactDetails);
+
+ mockMvc.perform(post("/service-centres/{serviceCentreId}/v1/contact-details", SERVICE_CENTRE_ID)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(contactDetails)))
+ .andExpect(status().isCreated())
+ .andExpect(jsonPath("$.id").value(CONTACT_ID.toString()));
+ }
+
+ @Test
+ void putContactDetailUpdatesContactDetail() throws Exception {
+ ServiceCentreContactDetails contactDetails = buildContactDetails();
+ when(serviceCentreContactDetailsService.updateContactDetail(
+ eq(SERVICE_CENTRE_ID),
+ eq(CONTACT_ID),
+ any(ServiceCentreContactDetails.class)
+ )).thenReturn(contactDetails);
+
+ mockMvc.perform(put("/service-centres/{serviceCentreId}/v1/contact-details/{contactId}",
+ SERVICE_CENTRE_ID,
+ CONTACT_ID)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(contactDetails)))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(CONTACT_ID.toString()));
+ }
+
+ @Test
+ void deleteContactDetailReturnsNoContent() throws Exception {
+ mockMvc.perform(delete("/service-centres/{serviceCentreId}/v1/contact-details/{contactId}",
+ SERVICE_CENTRE_ID,
+ CONTACT_ID))
+ .andExpect(status().isNoContent());
+ }
+
+ @Test
+ void deleteContactDetailReturnsNotFound() throws Exception {
+ doThrow(new NotFoundException("Missing"))
+ .when(serviceCentreContactDetailsService).deleteContactDetail(SERVICE_CENTRE_ID, CONTACT_ID);
+
+ mockMvc.perform(delete("/service-centres/{serviceCentreId}/v1/contact-details/{contactId}",
+ SERVICE_CENTRE_ID,
+ CONTACT_ID))
+ .andExpect(status().isNotFound());
+ }
+
+ private ServiceCentreContactDetails buildContactDetails() {
+ return ServiceCentreContactDetails.builder()
+ .id(CONTACT_ID)
+ .serviceCentreId(SERVICE_CENTRE_ID)
+ .explanation("General enquiries")
+ .email("test@example.com")
+ .phoneNumber("020 123 456")
+ .build();
+ }
+}
diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreControllerTest.java b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreControllerTest.java
new file mode 100644
index 000000000..4736e23d7
--- /dev/null
+++ b/src/integrationTest/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreControllerTest.java
@@ -0,0 +1,130 @@
+package uk.gov.hmcts.reform.fact.data.api.controllers;
+
+import io.qameta.allure.Feature;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
+import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.bean.override.mockito.MockitoBean;
+import org.springframework.test.web.servlet.MockMvc;
+import tools.jackson.databind.ObjectMapper;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentre;
+import uk.gov.hmcts.reform.fact.data.api.entities.types.CatchmentType;
+import uk.gov.hmcts.reform.fact.data.api.errorhandling.exceptions.NotFoundException;
+import uk.gov.hmcts.reform.fact.data.api.services.ServiceCentreService;
+
+import java.util.UUID;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@Feature("Service Centre Controller")
+@DisplayName("Service Centre Controller")
+@WebMvcTest(ServiceCentreController.class)
+@AutoConfigureMockMvc(addFilters = false)
+@ActiveProfiles("test")
+class ServiceCentreControllerTest {
+
+ private static final UUID SERVICE_CENTRE_ID = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
+ private static final String SERVICE_CENTRE_NAME = "Test Service Centre";
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @MockitoBean
+ private ServiceCentreService serviceCentreService;
+
+ @Test
+ @DisplayName("GET /service-centres/{serviceCentreId}/v1 returns service centre")
+ void getServiceCentreByIdReturnsServiceCentre() throws Exception {
+ ServiceCentre serviceCentre = buildServiceCentre();
+
+ when(serviceCentreService.getServiceCentreById(SERVICE_CENTRE_ID)).thenReturn(serviceCentre);
+
+ mockMvc.perform(get("/service-centres/{serviceCentreId}/v1", SERVICE_CENTRE_ID))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(SERVICE_CENTRE_ID.toString()))
+ .andExpect(jsonPath("$.name").value(SERVICE_CENTRE_NAME));
+ }
+
+ @Test
+ @DisplayName("GET /service-centres/{serviceCentreId}/v1 returns 400 for invalid UUID")
+ void getServiceCentreByIdReturnsBadRequestForInvalidUuid() throws Exception {
+ mockMvc.perform(get("/service-centres/{serviceCentreId}/v1", "invalid-uuid"))
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ @DisplayName("GET /service-centres/{serviceCentreId}/v1 returns 404 when missing")
+ void getServiceCentreByIdReturnsNotFound() throws Exception {
+ when(serviceCentreService.getServiceCentreById(SERVICE_CENTRE_ID))
+ .thenThrow(new NotFoundException("Service centre not found"));
+
+ mockMvc.perform(get("/service-centres/{serviceCentreId}/v1", SERVICE_CENTRE_ID))
+ .andExpect(status().isNotFound());
+ }
+
+ @Test
+ @DisplayName("GET /service-centres/name/v1 returns service centre for exact name")
+ void getServiceCentreByNameReturnsServiceCentre() throws Exception {
+ ServiceCentre serviceCentre = buildServiceCentre();
+
+ when(serviceCentreService.getServiceCentreByName(SERVICE_CENTRE_NAME)).thenReturn(serviceCentre);
+
+ mockMvc.perform(get("/service-centres/name/v1").param("name", SERVICE_CENTRE_NAME))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(SERVICE_CENTRE_ID.toString()))
+ .andExpect(jsonPath("$.name").value(SERVICE_CENTRE_NAME));
+ }
+
+ @Test
+ @DisplayName("POST /service-centres/v1 creates service centre")
+ void createServiceCentreReturnsCreated() throws Exception {
+ ServiceCentre serviceCentre = buildServiceCentre();
+
+ when(serviceCentreService.createServiceCentre(any(ServiceCentre.class))).thenReturn(serviceCentre);
+
+ mockMvc.perform(post("/service-centres/v1")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(serviceCentre)))
+ .andExpect(status().isCreated())
+ .andExpect(jsonPath("$.id").value(SERVICE_CENTRE_ID.toString()));
+ }
+
+ @Test
+ @DisplayName("PUT /service-centres/{serviceCentreId}/v1 updates service centre")
+ void updateServiceCentreReturnsUpdated() throws Exception {
+ ServiceCentre serviceCentre = buildServiceCentre();
+
+ when(serviceCentreService.updateServiceCentre(eq(SERVICE_CENTRE_ID), any(ServiceCentre.class)))
+ .thenReturn(serviceCentre);
+
+ mockMvc.perform(put("/service-centres/{serviceCentreId}/v1", SERVICE_CENTRE_ID)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(serviceCentre)))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(SERVICE_CENTRE_ID.toString()));
+ }
+
+ private ServiceCentre buildServiceCentre() {
+ return ServiceCentre.builder()
+ .id(SERVICE_CENTRE_ID)
+ .name(SERVICE_CENTRE_NAME)
+ .slug("test-service-centre")
+ .open(true)
+ .catchmentType(CatchmentType.REGIONAL)
+ .build();
+ }
+}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAddressController.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAddressController.java
new file mode 100644
index 000000000..88a096f62
--- /dev/null
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAddressController.java
@@ -0,0 +1,149 @@
+package uk.gov.hmcts.reform.fact.data.api.controllers;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentreAddress;
+import uk.gov.hmcts.reform.fact.data.api.security.SecuredFactRestController;
+import uk.gov.hmcts.reform.fact.data.api.services.ServiceCentreAddressService;
+import uk.gov.hmcts.reform.fact.data.api.validation.annotations.ValidUUID;
+
+import java.util.List;
+import java.util.UUID;
+
+@SecuredFactRestController(
+ name = "Service Centre Addresses",
+ description = "Operations related to addresses available for service centres"
+)
+@RequestMapping("/service-centres/{serviceCentreId}")
+@RequiredArgsConstructor
+@SuppressWarnings("java:S4684")
+public class ServiceCentreAddressController {
+
+ private final ServiceCentreAddressService serviceCentreAddressService;
+
+ @GetMapping("/v1/address")
+ @Operation(
+ summary = "Get addresses for a service centre",
+ description = "Fetch all addresses associated with the supplied service centre ID."
+ )
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Successfully retrieved addresses"),
+ @ApiResponse(responseCode = "400", description = "Invalid service centre ID supplied"),
+ @ApiResponse(responseCode = "404", description = "Service centre not found")
+ })
+ public ResponseEntity> getAddresses(
+ @Parameter(description = "UUID of the service centre", required = true)
+ @ValidUUID @PathVariable String serviceCentreId
+ ) {
+ return ResponseEntity.ok(
+ serviceCentreAddressService.getAddresses(UUID.fromString(serviceCentreId))
+ );
+ }
+
+ @GetMapping("/v1/address/{addressId}")
+ @Operation(
+ summary = "Get a specific address for a service centre",
+ description = "Fetch a single address by service centre and address identifier."
+ )
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Successfully retrieved the address"),
+ @ApiResponse(responseCode = "400", description = "Invalid service centre ID or address ID supplied"),
+ @ApiResponse(responseCode = "404", description = "Address or service centre not found")
+ })
+ public ResponseEntity getAddress(
+ @Parameter(description = "UUID of the service centre", required = true)
+ @ValidUUID @PathVariable String serviceCentreId,
+ @Parameter(description = "UUID of the address", required = true) @ValidUUID @PathVariable String addressId
+ ) {
+ return ResponseEntity.ok(
+ serviceCentreAddressService.getAddress(
+ UUID.fromString(serviceCentreId),
+ UUID.fromString(addressId)
+ )
+ );
+ }
+
+ @PostMapping("/v1/address")
+ @Operation(
+ summary = "Create address for a service centre",
+ description = "Create a new address entry for the supplied service centre."
+ )
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "201", description = "Successfully created the address"),
+ @ApiResponse(responseCode = "400", description = "Invalid service centre ID supplied or invalid request body"),
+ @ApiResponse(responseCode = "404", description = "Service centre not found")
+ })
+ @PreAuthorize("@authService.isAdmin()")
+ public ResponseEntity createAddress(
+ @Parameter(description = "UUID of the service centre", required = true)
+ @ValidUUID @PathVariable String serviceCentreId,
+ @Parameter(description = "Address to create", required = true)
+ @Valid @RequestBody ServiceCentreAddress serviceCentreAddress
+ ) {
+ return ResponseEntity
+ .status(HttpStatus.CREATED)
+ .body(serviceCentreAddressService.createAddress(UUID.fromString(serviceCentreId), serviceCentreAddress));
+ }
+
+ @PutMapping("/v1/address/{addressId}")
+ @Operation(
+ summary = "Update address for a service centre",
+ description = "Update an existing address entry for the supplied service centre."
+ )
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Successfully updated the address"),
+ @ApiResponse(responseCode = "400",
+ description = "Invalid service centre ID or address ID supplied, or invalid request body"),
+ @ApiResponse(responseCode = "404", description = "Address or service centre not found")
+ })
+ @PreAuthorize("@authService.isAdmin()")
+ public ResponseEntity updateServiceCentreAddress(
+ @Parameter(description = "UUID of the service centre", required = true)
+ @ValidUUID @PathVariable String serviceCentreId,
+ @Parameter(description = "UUID of the address", required = true) @ValidUUID @PathVariable String addressId,
+ @Parameter(description = "Updated address", required = true)
+ @Valid @RequestBody ServiceCentreAddress serviceCentreAddress
+ ) {
+ return ResponseEntity.ok(
+ serviceCentreAddressService.updateAddress(
+ UUID.fromString(serviceCentreId),
+ UUID.fromString(addressId),
+ serviceCentreAddress
+ )
+ );
+ }
+
+ @DeleteMapping("/v1/address/{addressId}")
+ @Operation(
+ summary = "Delete address for a service centre",
+ description = "Remove an existing address entry for the supplied service centre."
+ )
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "204", description = "Successfully deleted the address"),
+ @ApiResponse(responseCode = "400", description = "Invalid service centre ID or address ID supplied"),
+ @ApiResponse(responseCode = "404", description = "Address or service centre not found")
+ })
+ @PreAuthorize("@authService.isAdmin()")
+ public ResponseEntity deleteServiceCentreAddress(
+ @Parameter(description = "UUID of the service centre", required = true)
+ @ValidUUID @PathVariable String serviceCentreId,
+ @Parameter(description = "UUID of the address", required = true) @ValidUUID @PathVariable String addressId
+ ) {
+ serviceCentreAddressService.deleteAddress(UUID.fromString(serviceCentreId), UUID.fromString(addressId));
+ return ResponseEntity.noContent().build();
+ }
+}
diff --git a/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAreasOfLawController.java b/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAreasOfLawController.java
new file mode 100644
index 000000000..f1b7ddca9
--- /dev/null
+++ b/src/main/java/uk/gov/hmcts/reform/fact/data/api/controllers/ServiceCentreAreasOfLawController.java
@@ -0,0 +1,77 @@
+package uk.gov.hmcts.reform.fact.data.api.controllers;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import uk.gov.hmcts.reform.fact.data.api.entities.AreaOfLawType;
+import uk.gov.hmcts.reform.fact.data.api.entities.ServiceCentreAreasOfLaw;
+import uk.gov.hmcts.reform.fact.data.api.security.SecuredFactRestController;
+import uk.gov.hmcts.reform.fact.data.api.services.ServiceCentreAreasOfLawService;
+import uk.gov.hmcts.reform.fact.data.api.validation.annotations.ValidUUID;
+
+import java.util.Map;
+import java.util.UUID;
+
+@SecuredFactRestController(
+ name = "Service Centre Areas Of Law",
+ description = "Operations related to Service Centre Areas Of Law"
+)
+@RequestMapping("/service-centres/{serviceCentreId}")
+@RequiredArgsConstructor
+public class ServiceCentreAreasOfLawController {
+
+ private final ServiceCentreAreasOfLawService serviceCentreAreasOfLawService;
+
+ @GetMapping("/v1/areas-of-law")
+ @Operation(
+ summary = "Get Areas Of Law map with boolean by service centre ID",
+ description = "Fetch Areas Of Law with availability boolean for a given service centre."
+ )
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Successfully retrieved Areas Of Law"),
+ @ApiResponse(responseCode = "400", description = "Invalid service centre ID supplied"),
+ @ApiResponse(responseCode = "404", description = "Service centre not found")
+ })
+ public ResponseEntity