Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,14 @@ void shouldGetCounterServiceOpeningHours() throws Exception {
final Response getResponse = http.doGet("/courts/" + courtId + "/v1/opening-hours/counter-service");
assertThat(getResponse.statusCode()).isEqualTo(OK.value());

final CourtCounterServiceOpeningHours retrievedHours = mapper.readValue(
final List<CourtCounterServiceOpeningHours> retrievedHoursList = mapper.readValue(
getResponse.asString(),
CourtCounterServiceOpeningHours.class
mapper.getTypeFactory().constructCollectionType(List.class, CourtCounterServiceOpeningHours.class)
);

assertThat((retrievedHoursList)).hasSize(1);
final CourtCounterServiceOpeningHours retrievedHours = retrievedHoursList.getFirst();

assertThat(retrievedHours.getOpeningTimesDetails()).hasSize(5);
assertThat(retrievedHours.getOpeningTimesDetails().getFirst().getOpeningTime())
.isEqualTo(LocalTime.of(9, 0));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,17 @@ void getOpeningHoursByIdInvalidUUID() throws Exception {
}

@Test
@DisplayName("GET /courts/{courtId}/v1/opening-hours/counter-service returns opening hours successfully")
@DisplayName("GET /courts/{courtId}/v1/opening-hours/counter-service/{counterServiceOpeningHoursId}"
+ "returns counter service opening hours successfully")
void getCounterServiceOpeningHoursReturnsSuccessfully() throws Exception {
when(courtOpeningHoursService.getCounterServiceOpeningHoursByCourtId(courtId))
when(courtOpeningHoursService.getCounterServiceOpeningHoursById(courtId, counterServiceOpeningHours.getId()))
.thenReturn(counterServiceOpeningHours);

mockMvc.perform(get("/courts/{courtId}/v1/opening-hours/counter-service", courtId))
mockMvc.perform(
get(
"/courts/{courtId}/v1/opening-hours/counter-service/{counterServiceOpeningHoursId}",
courtId,
counterServiceOpeningHours.getId()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.openingTimesDetails[0].dayOfWeek")
.value(counterServiceOpeningHours
Expand Down Expand Up @@ -562,8 +567,9 @@ void deleteOpeningHoursInvalidUUID() throws Exception {
@DisplayName("DELETE /courts/{courtId}/v1/opening-hours/counter-service deletes opening hours successfully")
void deleteCounterServiceOpeningHoursDeletesSuccessfully() throws Exception {
mockMvc.perform(delete(
"/courts/{courtId}/v1/opening-hours/counter-service",
courtId
"/courts/{courtId}/v1/opening-hours/counter-service/{counterServiceId}",
courtId,
counterServiceOpeningHours.getId()
))
.andExpect(status().isOk());
}
Expand All @@ -573,11 +579,12 @@ void deleteCounterServiceOpeningHoursDeletesSuccessfully() throws Exception {
void deleteCounterServiceOpeningHoursNonExistentCourtReturnsNotFound() throws Exception {
doThrow(new NotFoundException("Court not found"))
.when(courtOpeningHoursService)
.deleteCourtCounterServiceOpeningHours(nonExistentCourtId);
.deleteCourtCounterServiceOpeningHours(nonExistentCourtId, counterServiceOpeningHours.getId());

mockMvc.perform(delete(
"/courts/{courtId}/v1/opening-hours/counter-service",
nonExistentCourtId
"/courts/{courtId}/v1/opening-hours/counter-service/{counterServiceId}",
nonExistentCourtId,
counterServiceOpeningHours.getId()
))
.andExpect(status().isNotFound());
}
Expand All @@ -586,8 +593,9 @@ void deleteCounterServiceOpeningHoursNonExistentCourtReturnsNotFound() throws Ex
@DisplayName("DELETE /courts/{courtId}/v1/opening-hours/counter-service returns 400 for invalid UUID")
void deleteCounterServiceOpeningHoursInvalidUUID() throws Exception {
mockMvc.perform(delete(
"/courts/{courtId}/v1/opening-hours/counter-service",
"invalid-uuid"
"/courts/{courtId}/v1/opening-hours/counter-service/{counterServiceId}",
"invalid-uuid",
counterServiceOpeningHours.getId()
))
.andExpect(status().isBadRequest());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,32 @@ public ResponseEntity<CourtOpeningHours> getOpeningHoursById(
@ApiResponse(responseCode = "400", description = "Invalid court ID supplied"),
@ApiResponse(responseCode = "404", description = "Court not found")
})
public ResponseEntity<CourtCounterServiceOpeningHours> getCounterServiceOpeningHoursByCourtId(
public ResponseEntity<List<CourtCounterServiceOpeningHours>> getCounterServiceOpeningHoursByCourtId(
@Parameter(description = "UUID of the court", required = true) @ValidUUID @PathVariable String courtId) {
return ResponseEntity.ok(courtOpeningHoursService.getCounterServiceOpeningHoursByCourtId(
UUID.fromString(courtId))
);
}

@GetMapping("/v1/opening-hours/counter-service/{counterServiceId}")
@Operation(
summary = "Get court counter service opening hours by court ID and counter service ID",
description = "Fetch counter service opening hours for a given court."
+ "Returns 204 if no counter service opening hours exist for the court with this ID."
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved court counter service opening hours"),
@ApiResponse(responseCode = "204", description = "No counter service found for the court with this ID"),
@ApiResponse(responseCode = "400", description = "Invalid court ID or court counter service ID supplied"),
@ApiResponse(responseCode = "404", description = "Court or counter service opening hours not found")
})
public ResponseEntity<CourtCounterServiceOpeningHours> getCounterServiceOpeningHoursById(
@Parameter(description = "UUID of the court", required = true) @ValidUUID @PathVariable String courtId,
@Parameter(description = "UUID of the counter service opening hours", required = true)
@ValidUUID @PathVariable String counterServiceId) {
return ResponseEntity.ok(
courtOpeningHoursService.getCounterServiceOpeningHoursByCourtId(UUID.fromString(courtId)));
courtOpeningHoursService
.getCounterServiceOpeningHoursById(UUID.fromString(courtId), UUID.fromString(counterServiceId)));
}

@PutMapping("/v1/opening-hours")
Expand Down Expand Up @@ -120,7 +142,7 @@ public ResponseEntity<CourtOpeningHours> setOpeningHours(
description = "Creates a opening hours for a court or updates the existing one."
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully created/updated opening hours"),
@ApiResponse(responseCode = "200", description = "Successfully created/updated counter service opening hours"),
@ApiResponse(responseCode = "204", description = "No Content"),
@ApiResponse(responseCode = "400", description = "Invalid court ID or request body"),
@ApiResponse(responseCode = "404", description = "Court or court type not found")
Expand Down Expand Up @@ -154,7 +176,7 @@ public ResponseEntity<Void> deleteOpeningHours(
return ResponseEntity.ok().body(null);
}

@DeleteMapping("/v1/opening-hours/counter-service")
@DeleteMapping("/v1/opening-hours/counter-service/{counterServiceId}")
@Operation(
summary = "Delete counter service opening hours for a court",
description = "Deletes counter service opening hours for a court."
Expand All @@ -166,8 +188,12 @@ public ResponseEntity<Void> deleteOpeningHours(
})
@PreAuthorize("@authService.isAdmin()")
public ResponseEntity<Void> deleteCounterServiceOpeningHours(
@Parameter(description = "UUID of the court", required = true) @ValidUUID @PathVariable String courtId) {
courtOpeningHoursService.deleteCourtCounterServiceOpeningHours(UUID.fromString(courtId));
@Parameter(description = "UUID of the court", required = true) @ValidUUID @PathVariable String courtId,
@Parameter(description = "UUID of the counter service", required = true)
@ValidUUID @PathVariable String counterServiceId) {
courtOpeningHoursService.deleteCourtCounterServiceOpeningHours(
UUID.fromString(courtId), UUID.fromString(counterServiceId)
);
return ResponseEntity.ok().body(null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import uk.gov.hmcts.reform.fact.data.api.entities.CourtCounterServiceOpeningHours;

import java.util.List;
import java.util.Optional;
import java.util.UUID;

Expand All @@ -12,7 +13,7 @@
public interface CourtCounterServiceOpeningHoursRepository
extends JpaRepository<CourtCounterServiceOpeningHours, UUID> {

Optional<CourtCounterServiceOpeningHours> findByCourtId(UUID courtId);
List<CourtCounterServiceOpeningHours> findByCourtId(UUID courtId);

void deleteByCourtId(UUID courtId);
Optional<CourtCounterServiceOpeningHours> findByCourtIdAndId(UUID courtId, UUID counterServiceOpeningHourId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,39 @@ public CourtOpeningHours getOpeningHoursById(UUID courtId, UUID openingHoursId)
"No opening hour found for court ID: " + courtId + " with ID: " + openingHoursId));
}

/**
* Get a list of counter service opening hours by court ID.
*
* @param courtId The court ID to find the counter service opening hours for.
* @return The list of counter service opening hours.
* @throws CourtResourceNotFoundException if no counter service record exists for the court or the list is empty.
*/
public List<CourtCounterServiceOpeningHours> getCounterServiceOpeningHoursByCourtId(UUID courtId) {
List<CourtCounterServiceOpeningHours> result =
courtCounterServiceOpeningHoursRepository.findByCourtId(courtService.getCourtById(courtId).getId());
if (result.isEmpty()) {
throw new CourtResourceNotFoundException(
"No counter service opening hours found for court ID: " + courtId);
}
return result;
}

/**
* Get counter-service opening hours by court ID.
* A court will only ever have zero or one counter-service opening hours record.
* Get counter-service opening hours by court ID and counter service ID.
*
* @param courtId The court ID to find the counter-service opening hours for.
* @param counterServiceId The ID for the counter service opening hours to find.
* @return The counter-service opening hours record.
* @throws CourtResourceNotFoundException if no counter-service opening hours record exists for the court.
*/
public CourtCounterServiceOpeningHours getCounterServiceOpeningHoursByCourtId(UUID courtId) {
public CourtCounterServiceOpeningHours getCounterServiceOpeningHoursById(
final UUID courtId, final UUID counterServiceId) {
return courtCounterServiceOpeningHoursRepository
.findByCourtId(courtService.getCourtById(courtId).getId())
.findByCourtIdAndId(courtService.getCourtById(courtId).getId(), counterServiceId)
.orElseThrow(
() -> new CourtResourceNotFoundException(
"No counter service opening hours found for court ID: " + courtId));
"No counter service opening hours found for court ID: " + courtId + " with ID: "
+ counterServiceId));
}

/**
Expand Down Expand Up @@ -147,7 +165,7 @@ public CourtCounterServiceOpeningHours setCounterServiceOpeningHours(
.map(this::validateCourtTypeIds)
.ifPresent(courtCounterServiceOpeningHours::setCourtTypes);

courtCounterServiceOpeningHoursRepository.findByCourtId(courtId)
courtCounterServiceOpeningHoursRepository.findByCourtIdAndId(courtId, courtCounterServiceOpeningHours.getId())
.ifPresent(existing -> courtCounterServiceOpeningHours.setId(existing.getId()));

return courtCounterServiceOpeningHoursRepository.save(courtCounterServiceOpeningHours);
Expand All @@ -165,11 +183,13 @@ public void deleteCourtOpeningHours(UUID courtId, UUID openingHoursId) {

/**
* Delete court counter service opening hours.
* @param courtId The ID of the court to delete counter service opening hours for.
* @param counterServiceId The ID of the counter service to delete counter service opening hours for court courtId.
*/
@Transactional
public void deleteCourtCounterServiceOpeningHours(UUID courtId) {
courtCounterServiceOpeningHoursRepository.deleteByCourtId(courtService.getCourtById(courtId).getId());
public void deleteCourtCounterServiceOpeningHours(final UUID courtId, final UUID counterServiceId) {
log.info("Deleting court counter service opening hours for court ID: {} and counter service ID: {}",
courtId, counterServiceId);
courtCounterServiceOpeningHoursRepository.deleteById(counterServiceId);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,13 @@ void getOpeningHoursByIdThrowsIllegalArgumentExceptionForInvalidUUID() {
@Test
void getCounterServiceOpeningHoursByCourtIdReturns200() {
when(courtOpeningHoursService.getCounterServiceOpeningHoursByCourtId(COURT_ID))
.thenReturn(counterServiceOpeningHours);
.thenReturn(List.of(counterServiceOpeningHours));

ResponseEntity<CourtCounterServiceOpeningHours> response
ResponseEntity<List<CourtCounterServiceOpeningHours>> response
= courtOpeningHoursController.getCounterServiceOpeningHoursByCourtId(COURT_ID.toString());

assertThat(response.getStatusCode()).as(RESPONSE_STATUS_MESSAGE).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).as(RESPONSE_BODY_MESSAGE).isEqualTo(counterServiceOpeningHours);
assertThat(response.getBody()).as(RESPONSE_BODY_MESSAGE).isEqualTo(List.of(counterServiceOpeningHours));
}

@Test
Expand Down Expand Up @@ -238,7 +238,7 @@ void getCounterServiceOpeningHoursThrowsCourtNotFoundException() {
void getCounterServiceOpeningHoursThrowsIllegalArgumentExceptionForInvalidUUID() {
assertThrows(
IllegalArgumentException.class, () ->
courtOpeningHoursController.getCounterServiceOpeningHoursByCourtId(INVALID_UUID)
courtOpeningHoursController.getCounterServiceOpeningHoursById(INVALID_UUID, INVALID_UUID)
);
}

Expand Down Expand Up @@ -351,7 +351,8 @@ void deleteOpeningHoursByTypeIdThrowsIllegalArgumentException() {
@Test
void deleteCourtCounterServiceOpeningHoursReturns200() {
ResponseEntity<Void> response = courtOpeningHoursController.deleteCounterServiceOpeningHours(
COURT_ID.toString()
COURT_ID.toString(),
counterServiceOpeningHours.getId().toString()
);
assertThat(response.getStatusCode()).as(RESPONSE_STATUS_MESSAGE).isEqualTo(HttpStatus.OK);
}
Expand All @@ -360,12 +361,13 @@ void deleteCourtCounterServiceOpeningHoursReturns200() {
void deleteCourtCounterServiceOpeningHoursThrowsNotFoundException() {
doThrow(new NotFoundException(COURT_NOT_FOUND_MESSAGE))
.when(courtOpeningHoursService)
.deleteCourtCounterServiceOpeningHours(UNKNOWN_COURT_ID);
.deleteCourtCounterServiceOpeningHours(UNKNOWN_COURT_ID, UNKNOWN_ID);

assertThrows(
NotFoundException.class, () ->
courtOpeningHoursController.deleteCounterServiceOpeningHours(
UNKNOWN_COURT_ID.toString()
UNKNOWN_COURT_ID.toString(),
UNKNOWN_ID.toString()
)
);
}
Expand All @@ -375,7 +377,7 @@ void deleteCounterServiceOpeningHoursThrowsIllegalArgumentException() {
assertThrows(
IllegalArgumentException.class, () ->
courtOpeningHoursController
.deleteCounterServiceOpeningHours(INVALID_UUID)
.deleteCounterServiceOpeningHours(INVALID_UUID, INVALID_UUID)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,18 +202,18 @@ void getOpeningHoursThrowsExceptionWhenOpeningHourTypeDoesNotExist() {
void getCounterServiceOpeningHoursByCourtIdReturnsOpeningHoursWhenFound() {
when(courtService.getCourtById(courtId)).thenReturn(court);
when(courtCounterServiceOpeningHoursRepository.findByCourtId(courtId))
.thenReturn(Optional.of(counterServiceOpeningHours));
.thenReturn(List.of(counterServiceOpeningHours));

CourtCounterServiceOpeningHours result =
List<CourtCounterServiceOpeningHours> result =
courtOpeningHoursService.getCounterServiceOpeningHoursByCourtId(courtId);

assertThat(result).isEqualTo(counterServiceOpeningHours);
assertThat(result).isEqualTo(List.of(counterServiceOpeningHours));
}

@Test
void getCounterServiceOpeningHoursByCourtIdThrowsExceptionWhenNotFound() {
when(courtService.getCourtById(courtId)).thenReturn(court);
when(courtCounterServiceOpeningHoursRepository.findByCourtId(courtId)).thenReturn(Optional.empty());
when(courtCounterServiceOpeningHoursRepository.findByCourtId(courtId)).thenReturn(List.of());

assertThrows(
CourtResourceNotFoundException.class,
Expand All @@ -230,6 +230,40 @@ void getCounterServiceOpeningHoursThrowsExceptionWhenCourtDoesNotExist() {
);
}

@Test
void getCounterServiceOpeningHoursByIdReturnsCounterServiceOpeningHoursWhenFound() {
when(courtService.getCourtById(courtId)).thenReturn(court);
when(courtCounterServiceOpeningHoursRepository.findByCourtIdAndId(courtId, counterServiceOpeningHours.getId()))
.thenReturn(Optional.of(counterServiceOpeningHours));

CourtCounterServiceOpeningHours result = courtOpeningHoursService
.getCounterServiceOpeningHoursById(courtId, counterServiceOpeningHours.getId());

assertThat(result).isEqualTo(counterServiceOpeningHours);
}

@Test
void getCounterServiceOpeningHoursByIdThrowsExceptionWhenNotFound() {
when(courtService.getCourtById(courtId)).thenReturn(court);
when(courtCounterServiceOpeningHoursRepository.findByCourtIdAndId(courtId, counterServiceOpeningHours.getId()))
.thenReturn(Optional.empty());

final UUID counterServiceId = counterServiceOpeningHours.getId();
assertThrows(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We have a few of these in the code, but if you want to get shot of the sonar warning, I think you just need to move the counterServiceOpeningHours.getId() call outside of the lambda. I think the complaint is that from a purist perspective, that could potentially throw an NPE.

CourtResourceNotFoundException.class,
() -> courtOpeningHoursService.getCounterServiceOpeningHoursById(courtId, counterServiceId));
}

@Test
void getCounterServiceOpeningHoursByIdThrowsExceptionWhenCourtDoesNotExist() {
when(courtService.getCourtById(courtId)).thenThrow(new NotFoundException(COURT_NOT_FOUND_MESSAGE));

final UUID counterServiceId = counterServiceOpeningHours.getId();
assertThrows(NotFoundException.class, () ->
courtOpeningHoursService.getCounterServiceOpeningHoursById(courtId, counterServiceId)
);
}

@Test
void setOpeningHoursSuccessfullyCreatesNewOpeningHours() {
when(courtService.getCourtById(courtId)).thenReturn(court);
Expand Down Expand Up @@ -474,5 +508,12 @@ void deleteCourtOpeningHoursSuccessfullyDeletesHours() {

verify(courtOpeningHoursRepository).deleteById(openingHours.getId());
}

@Test
void deleteCounterServiceOpeningHoursSuccessfullyDeletesHours() {
courtOpeningHoursService.deleteCourtCounterServiceOpeningHours(courtId, counterServiceOpeningHours.getId());

verify(courtCounterServiceOpeningHoursRepository).deleteById(counterServiceOpeningHours.getId());
}
}

Loading