Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
82886de
DTSRD-5695.LRD Court Venue - Data model updates
SabinaHMCTS Feb 12, 2026
9482f1b
DTSRD-5695.LRD Court Venue - Data model updates
SabinaHMCTS Feb 12, 2026
c84503b
DTSRD-4052 . Adding extra column to court venue table
SabinaHMCTS Feb 16, 2026
e48047b
DTSRD-4052 . Adding extra column to court venue table
SabinaHMCTS Feb 16, 2026
d62ed52
DTSRD-4052 . Adding extra column to court venue table
SabinaHMCTS Feb 16, 2026
8aa63c5
DTSRD-4052 . Adding extra column to court venue table
SabinaHMCTS Feb 16, 2026
07f627d
DTSRD-4052 . Adding extra column to court venue table
SabinaHMCTS Feb 16, 2026
7df61c5
DTSRD-5699 . LRD Court Venue - Updates to retrieveCourtVenuesBySearch…
SabinaHMCTS Feb 25, 2026
7408a01
DTSRD-5699 . LRD Court Venue - Updates to retrieveCourtVenuesBySearch…
SabinaHMCTS Feb 25, 2026
5dab66a
DTSRD-5699 . LRD Court Venue - Updates to retrieveCourtVenuesBySearch…
SabinaHMCTS Feb 25, 2026
961f8fc
DTSRD-5699 . LRD Court Venue - Updates to retrieveCourtVenuesBySearch…
SabinaHMCTS Feb 25, 2026
af81535
DTSRD-5699 . LRD Court Venue - Updates to retrieveCourtVenuesBySearch…
SabinaHMCTS Feb 25, 2026
d4d05da
DTSRD-5700.LRD Court Venue - Updates to retrieveCourtVenuesByServiceC…
SabinaHMCTS Feb 26, 2026
7369e4a
DTSRD-5699.LRD Court Venue - Updates to retrieveCourtVenuesBySearchSt…
SabinaHMCTS Feb 26, 2026
4982726
DTSRD-5699.LRD Court Venue - Updates to retrieveCourtVenuesBySearchSt…
SabinaHMCTS Feb 26, 2026
218c1fc
DTSRD-5699.LRD Court Venue - Updates to retrieveCourtVenuesBySearchSt…
SabinaHMCTS Feb 26, 2026
1bafd8a
Branch was auto-updated.
github-actions[bot] Mar 9, 2026
5600d49
Branch was auto-updated.
github-actions[bot] Mar 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ public void toReturnCourtVenuesBySearchString() {
courtType.setCourtVenues(courtVenues);

when(courtVenueRepository.findBySearchStringAndCourtTypeId(
any(),any(),any(),any(),any(),any())).thenReturn(courtVenues);
any(),any(),any(),any(),any(),any(),any())).thenReturn(courtVenues);
}

private CourtVenue getCourtVenue(Cluster cluster, Region region, CourtType courtType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static uk.gov.hmcts.reform.lrdapi.controllers.constants.ErrorConstants.INVALID_REQUEST_EXCEPTION;
import static uk.gov.hmcts.reform.lrdapi.controllers.constants.LocationRefConstants.EXCEPTION_MSG_INVALID_SERVICE_CODE;
import static uk.gov.hmcts.reform.lrdapi.controllers.constants.LocationRefConstants.SEARCH_STRING_VALUE_ERROR_MESSAGE;

@SerenityTest
Expand Down Expand Up @@ -71,6 +72,8 @@ void shouldRetrieveCourtVenues_By_SearchStringWithHyphen_WithStatusCode_200() {

}



@Test
@ToggleEnable(mapKey = mapKey, withFeature = true)
void shouldRetrieveCourtVenues_By_CourtTypeIdAndSearchString_WithStatusCode_200() {
Expand Down Expand Up @@ -125,6 +128,22 @@ void shouldRetrieveCourtVenues_By_NoSearchString_WithStatusCode_400() {
assertEquals(String.format(SEARCH_STRING_VALUE_ERROR_MESSAGE, ""), response.getErrorDescription());
}

@Test
@ToggleEnable(mapKey = mapKey, withFeature = true)
void shouldReturn400_WhenServiceCodeContainsSpecialCharacters() {
ErrorResponse response = (ErrorResponse)
lrdApiClient.retrieveResponseForGivenRequest(
HttpStatus.BAD_REQUEST,
"?search-string=Abe&service_code=AB$",
LrdCourtVenueResponse[].class,
path
);

assertThat(response).isNotNull();
assertEquals(INVALID_REQUEST_EXCEPTION.getErrorMessage(), response.getErrorMessage());
assertEquals(String.format(EXCEPTION_MSG_INVALID_SERVICE_CODE, "AB$"), response.getErrorDescription());
}

@Test
@ToggleEnable(mapKey = mapKey, withFeature = true)
void shouldReturnEmptyList_WhenNoDataFound() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ private static LrdBuildingLocationResponse buildingLocation1() {
.postcode("AB11 7KT")
.courtAddress("AB2, 49 HUNTLY STREET, ABERDEEN")
.courtVenueId("2")
.serviceCode("AAA3")
.build();

courtVenueResponses.add(response3);
Expand Down Expand Up @@ -581,6 +582,7 @@ private static LrdBuildingLocationResponse buildingLocation2() {
.locationType("Court")
.parentLocation("366559")
.welshVenueName("testVenue")
.serviceCode("AAA2")
.build();

courtVenueResponses.add(response3);
Expand Down Expand Up @@ -610,6 +612,7 @@ private static LrdBuildingLocationResponse buildingLocation3() {
.postcode("AB11 4RT")
.courtAddress("AB4, 51 HUNTLY STREET, ABERDEEN")
.courtVenueId("4")
.serviceCode("ABA4")
.build();

courtVenueResponses.add(response3);
Expand Down Expand Up @@ -647,6 +650,7 @@ private static LrdBuildingLocationResponse buildingLocation4() {
.postcode("AB11 3RP")
.courtAddress("AB9, 56 HUNTLY STREET, ABERDEEN")
.courtVenueId("9")
.serviceCode("AAA2")
.build();

courtVenueResponses.add(response3);
Expand Down Expand Up @@ -686,6 +690,7 @@ private LrdBuildingLocationResponse getBuildingLocationSampleResponse() {
.postcode("AB11 8IP")
.courtAddress("AB3, 50 HUNTLY STREET, ABERDEEN")
.courtVenueId("3")
.serviceCode("AAA6")
.build();

courtVenueResponses.add(response3);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -506,4 +506,64 @@ void shouldReturn200WhenParameterEpmIdsValueAllWithYAndSpacePassed() throws

assertThat(response).isNotEmpty().hasSize(1);
}

@Test
@SuppressWarnings("unchecked")
void retrieveCourtVenues_WithEpimmsId_ShouldReturnServiceCodeInResponse() throws JsonProcessingException {

final var response = (List<LrdCourtVenueResponse>)
lrdApiClient.retrieveCourtVenueResponseForGivenRequest("?epimms_id=123456789",
LrdCourtVenueResponse[].class, path);

assertThat(response).isNotEmpty().hasSize(1);
LrdCourtVenueResponse venueResponse = response.get(0);
assertNotNull(venueResponse.getServiceCode());
assertTrue(venueResponse.getServiceCode().matches("[A-Z0-9]+"));
}

@ParameterizedTest
@CsvSource({
"123456789,AAA6",
"123461,AAA2",
"123462,AAA3"
})
@SuppressWarnings("unchecked")
void retrieveCourtVenues_WithEpimmsId_ShouldReturnCorrectServiceCode(String epimmsId, String expectedServiceCode)
throws JsonProcessingException {

final var response = (List<LrdCourtVenueResponse>)
lrdApiClient.retrieveCourtVenueResponseForGivenRequest("?epimms_id=" + epimmsId,
LrdCourtVenueResponse[].class, path);

assertThat(response).isNotEmpty().hasSize(1);
assertEquals(expectedServiceCode, response.get(0).getServiceCode());
}

@Test
@SuppressWarnings("unchecked")
void retrieveCourtVenues_AllVenues_ShouldAllHaveServiceCode() throws JsonProcessingException {

final var response = (List<LrdCourtVenueResponse>)
lrdApiClient.retrieveCourtVenueResponseForGivenRequest("?epimms_id=ALL",
LrdCourtVenueResponse[].class, path);

assertThat(response).isNotEmpty();
assertTrue(response.stream().allMatch(venue -> venue.getServiceCode() != null
&& !venue.getServiceCode().isEmpty()));
}

@Test
@SuppressWarnings("unchecked")
void retrieveCourtVenues_WithEpimmsIdAndServiceCode_ShouldEnforceUniqueConstraint() throws JsonProcessingException {

final var response = (List<LrdCourtVenueResponse>)
lrdApiClient.retrieveCourtVenueResponseForGivenRequest("?epimms_id=123456789",
LrdCourtVenueResponse[].class, path);

assertThat(response).isNotEmpty().hasSize(1);
LrdCourtVenueResponse venueResponse = response.get(0);
assertNotNull(venueResponse.getEpimmsId());
assertNotNull(venueResponse.getServiceCode());
assertEquals("123456789", venueResponse.getEpimmsId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,22 @@ void shouldRetrieveCourtVenues_For_SearchString_And_CourtTypeId_WithStatusCode_2
assertEquals("Welsh External Short Name", response[12].getWelshExternalShortName());
}

@Test
@SuppressWarnings("unchecked")
void shouldRetrieveCourtVenues_For_SearchString_And_ServiceCode_WithStatusCode_200()
throws JsonProcessingException {
final var response = (LrdCourtVenueResponse[])
lrdApiClient.findCourtVenuesBySearchString(
"?search-string=Abe&service_code=AAA3",
LrdCourtVenueResponse[].class,
path
);

assertThat(response).isNotEmpty().hasSize(4);
assertTrue(Arrays.stream(response).allMatch(venue -> "AAA3".equals(venue.getServiceCode())));
responseVerification(new ArrayList<>(Arrays.asList(response)));
}

@ParameterizedTest
@ValueSource(strings = {"?search-string=abc--", "?search-string=ab__c", "?search-string=___c",
"?search-string=___", "?search-string=@@@", "?search-string=---", "?search-string='''",
Expand All @@ -109,6 +125,20 @@ void shouldReturn400_WhenInvalidParamsPassed(String parameter) throws JsonProces
assertThat(errorResponseMap).containsEntry(HTTP_STATUS_STR, HttpStatus.BAD_REQUEST);
}

@Test
@SuppressWarnings("unchecked")
void shouldReturn400_WhenServiceCodeContainsSpecialCharacters() throws JsonProcessingException {
Map<String, Object> errorResponseMap = (Map<String, Object>)
lrdApiClient.findCourtVenuesBySearchString(
"?search-string=Abe&service_code=AB$",
ErrorResponse.class,
path
);

assertNotNull(errorResponseMap);
assertThat(errorResponseMap).containsEntry(HTTP_STATUS_STR, HttpStatus.BAD_REQUEST);
}

@Test
@SuppressWarnings("unchecked")
void shouldReturnEmptyList_WhenCourtTypeIdIsInvalid() throws JsonProcessingException {
Expand Down Expand Up @@ -217,6 +247,7 @@ void shouldReturn200_WhenIsCaseManagementLocationContainY() throws JsonProcessin

assertThat(response).isNotEmpty().hasSize(3);
responseVerification(new ArrayList<>(Arrays.asList(response)));
assertServiceCodePresent(response);
}

@Test
Expand All @@ -231,6 +262,7 @@ void shouldReturn200_WhenIsCaseManagementLocationContainY_lowerCase() throws Jso

assertThat(response).isNotEmpty().hasSize(3);
responseVerification(new ArrayList<>(Arrays.asList(response)));
assertServiceCodePresent(response);
}

@Test
Expand All @@ -245,6 +277,7 @@ void shouldReturn200_WhenLocationTypeContainValue() throws JsonProcessingExcepti

assertThat(response).isNotEmpty().hasSize(1);
responseVerification(new ArrayList<>(Arrays.asList(response)));
assertServiceCodePresent(response);
}

@Test
Expand All @@ -258,6 +291,23 @@ void shouldReturn200_WhenLocationTypeContainValue_lowercase() throws JsonProcess
);
assertThat(response).isNotEmpty().hasSize(1);
responseVerification(new ArrayList<>(Arrays.asList(response)));
assertServiceCodePresent(response);
}

@Test
@SuppressWarnings("unchecked")
void shouldReturn200_WhenServiceCodeAndIsHearingLocationProvided() throws JsonProcessingException {
final var response = (LrdCourtVenueResponse[])
lrdApiClient.findCourtVenuesBySearchString(
"?search-string=Abe&service_code=AAA3&is_hearing_location=Y",
LrdCourtVenueResponse[].class,
path
);

assertThat(response).isNotEmpty();
assertTrue(Arrays.stream(response).allMatch(venue -> "AAA3".equals(venue.getServiceCode())));
assertTrue(Arrays.stream(response).allMatch(venue -> "Y".equalsIgnoreCase(venue.getIsHearingLocation())));
assertServiceCodePresent(response);
}

void responseVerification(ArrayList<LrdCourtVenueResponse> courtVenueResponse) {
Expand Down Expand Up @@ -287,4 +337,10 @@ void errorResponseVerification(Map<String, Object> errorResponseMap, String expe
expectedErrorDescription));
}

private void assertServiceCodePresent(LrdCourtVenueResponse[] response) {
assertTrue(Arrays.stream(response)
.map(LrdCourtVenueResponse::getServiceCode)
.allMatch(code -> code != null && !code.isBlank()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ private List<LrdCourtVenueResponse> buildCourtVenueResponses() {
.isHearingLocation("N")
.locationType("NBC")
.isTemporaryLocation("N")
.serviceCode("AAA6")
.build();

LrdCourtVenueResponse response2 = LrdCourtVenueResponse.builder()
Expand All @@ -149,6 +150,7 @@ private List<LrdCourtVenueResponse> buildCourtVenueResponses() {
.isHearingLocation("N")
.locationType("NBC")
.isTemporaryLocation("N")
.serviceCode("ABA4")
.build();

expectedCourtVenueResponses.add(response1);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-- Add service_code column to court_venue table
ALTER TABLE court_venue ADD COLUMN IF NOT EXISTS service_code VARCHAR(16);

-- Drop the existing unique constraint on (epimms_id, court_type_id)
ALTER TABLE court_venue DROP CONSTRAINT IF EXISTS court_location_unique;

-- Add a new unique constraint with epimms_id and service_code
ALTER TABLE court_venue ADD CONSTRAINT court_location_unique UNIQUE (epimms_id, service_code);

-- Add foreign key constraint for service_code
ALTER TABLE court_venue ADD CONSTRAINT court_venue_service_code_fk FOREIGN KEY (service_code) REFERENCES SERVICE (service_code);

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
UPDATE court_venue SET service_code = 'AAA2' WHERE court_venue_id = 1;
UPDATE court_venue SET service_code = 'AAA3' WHERE court_venue_id = 2;
UPDATE court_venue SET service_code = 'AAA6' WHERE court_venue_id = 3;
UPDATE court_venue SET service_code = 'ABA4' WHERE court_venue_id = 4;
UPDATE court_venue SET service_code = 'AAA2' WHERE court_venue_id = 5;
UPDATE court_venue SET service_code = 'AAA3' WHERE court_venue_id = 6;
UPDATE court_venue SET service_code = 'AAA6' WHERE court_venue_id = 7;
UPDATE court_venue SET service_code = 'ABA4' WHERE court_venue_id = 8;
UPDATE court_venue SET service_code = 'AAA2' WHERE court_venue_id = 9;
UPDATE court_venue SET service_code = 'AAA3' WHERE court_venue_id = 10;
UPDATE court_venue SET service_code = 'AAA6' WHERE court_venue_id = 11;
UPDATE court_venue SET service_code = 'ABA4' WHERE court_venue_id = 12;
UPDATE court_venue SET service_code = 'AAA2' WHERE court_venue_id = 13;
UPDATE court_venue SET service_code = 'AAA3' WHERE court_venue_id = 14;
UPDATE court_venue SET service_code = 'AAA6' WHERE court_venue_id = 15;
COMMIT;
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import static uk.gov.hmcts.reform.lrdapi.util.ValidationUtils.validateCourtTypeId;
import static uk.gov.hmcts.reform.lrdapi.util.ValidationUtils.validateCourtVenueFilters;
import static uk.gov.hmcts.reform.lrdapi.util.ValidationUtils.validateSearchString;
import static uk.gov.hmcts.reform.lrdapi.util.ValidationUtils.validateServiceCodes;


@RequestMapping(
Expand Down Expand Up @@ -265,6 +266,10 @@ public ResponseEntity<List<LrdCourtVenueResponse>> retrieveCourtVenuesBySearchSt
@Parameter(name = "court-type-id",
description = "Alphabets and Numeric values only allowed in comma separated format")
String courtTypeId,
@RequestParam(value = "service_code", required = false)
@Parameter(name = "service_code",
description = "Alphabets and Numeric values only allowed in comma separated format")
String serviceCode,
@RequestParam(value = "is_hearing_location", required = false)
@Parameter(name = "is_hearing_location",
description = "Allowed values are \"Y\" or \"N\"")
Expand All @@ -289,6 +294,10 @@ public ResponseEntity<List<LrdCourtVenueResponse>> retrieveCourtVenuesBySearchSt
validateCourtTypeId(courtTypeId);
}

if (StringUtils.isNotBlank(serviceCode)) {
validateServiceCodes(serviceCode);
}

CourtVenueRequestParam requestParam = CourtVenueRequestParam
.builder()
.isHearingLocation(isHearingLocation)
Expand All @@ -299,7 +308,7 @@ public ResponseEntity<List<LrdCourtVenueResponse>> retrieveCourtVenuesBySearchSt

log.info("{} : Calling retrieveCourtVenuesBySearchString", loggingComponentName);
var lrdCourtVenueResponses = courtVenueService.retrieveCourtVenuesBySearchString(
trimmedSearchString, courtTypeId, requestParam);
trimmedSearchString, courtTypeId, serviceCode, requestParam);
return ResponseEntity.status(HttpStatus.OK).body(lrdCourtVenueResponses);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ private LocationRefConstants() {
"No Building Location found for the given building location name: %s";
public static final String EXCEPTION_MSG_SERVICE_CODE_SPCL_CHAR = "Invalid service code. "
+ "Please provide service code without special characters";
public static final String EXCEPTION_MSG_INVALID_SERVICE_CODE = "Invalid service codes: %s";

public static final String LD_FLAG = "lrd_location_api";

Expand Down Expand Up @@ -77,6 +78,8 @@ private LocationRefConstants() {
public static final String COURT_TYPE_ID_START_END_WITH_COMMA =
"Invalid court type ids: %s";

public static final String SERVICE_CODE_START_END_WITH_COMMA =
"Invalid service codes: %s";

public static final String IS_HEARING_LOCATION_Y = "Y";
public static final String IS_HEARING_LOCATION_N = "N";
Expand Down Expand Up @@ -148,7 +151,7 @@ private LocationRefConstants() {
public static final String RET_LOC_VEN_NOTES_7 = "with the requested court_type_id are returned as a list.<br>";

public static final String RET_LOC_VEN_NOTES_7_1 = """
request param 'epimms_id' can be passed with 'court_type_id' . Court venues associated with the epimmsId and
request param 'epimms_id' can be passed with 'court_type_id' . Court venues associated with the epimmsId and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is removal of blank here known to not cause formatting issues?

court type id returned as list.
""";
public static final String RET_LOC_VEN_NOTES_8 = "For the request param 'region_id', the value needs to be a "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ public class LrdCourtVenueResponse implements Serializable {
@JsonProperty("epimms_id")
private String epimmsId;

@JsonProperty("service_code")
private String serviceCode;

@JsonProperty("site_name")
private String siteName;

Expand Down Expand Up @@ -151,6 +154,7 @@ public LrdCourtVenueResponse(CourtVenue courtVenue) {
this.courtType = courtVenue.getCourtType().getTypeOfCourt();
this.dxAddress = courtVenue.getDxAddress();
this.epimmsId = courtVenue.getEpimmsId();
this.serviceCode = courtVenue.getServiceCode();
this.openForPublic = Boolean.TRUE.equals(courtVenue.getOpenForPublic()) ? "YES" : "NO";
this.phoneNumber = courtVenue.getPhoneNumber();
this.welshCourtAddress = courtVenue.getWelshCourtAddress();
Expand Down
Loading