Skip to content

Commit

Permalink
Merge pull request #501 from Health-Education-England/feat/calculateC…
Browse files Browse the repository at this point in the history
…ctEndpointAndPlaceholder

feat: calculate CCT endpoint and dummy return value
  • Loading branch information
ReubenRobertsHEE authored Nov 21, 2024
2 parents 4735f53 + ed7ba38 commit 57b1451
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 1 deletion.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ plugins {
}

group = "uk.nhs.hee.trainee.details"
version = "1.16.1"
version = "1.17.0"

configurations {
compileOnly {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ void shouldNotGetCalculationWhenNotOwnedByUser() throws Exception {
@Test
void shouldGetCalculationWhenOwnedByUser() throws Exception {
UUID pmId = UUID.randomUUID();
LocalDate cctDate = LocalDate.of(2024, 1, 1);

CctCalculation entity = CctCalculation.builder()
.traineeId(TRAINEE_ID)
Expand Down Expand Up @@ -295,6 +296,7 @@ void shouldGetCalculationWhenOwnedByUser() throws Exception {
.andExpect(jsonPath("$.changes[0].type").value("LTFT"))
.andExpect(jsonPath("$.changes[0].startDate").value("2024-07-01"))
.andExpect(jsonPath("$.changes[0].wte").value(0.5))
.andExpect(jsonPath("$.cctDate").doesNotExist())
.andExpect(
jsonPath("$.created").value(entity.created().truncatedTo(ChronoUnit.MILLIS).toString()))
.andExpect(jsonPath("$.lastModified").value(
Expand Down Expand Up @@ -521,6 +523,7 @@ void shouldReturnCreatedCalculationJsonWhenRequestValid() throws Exception {
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.id").exists())
.andExpect(jsonPath("$.traineeId").doesNotExist())
.andExpect(jsonPath("$.cctDate").doesNotExist())
.andExpect(jsonPath("$.name").value("Test Calculation"))
.andExpect(jsonPath("$.created").exists())
.andExpect(jsonPath("$.lastModified").exists())
Expand Down Expand Up @@ -555,4 +558,25 @@ void shouldReturnLocationOfCreatedCalculationWhenRequestValid() throws Exception
.andExpect(jsonPath("$.id").value(id))
.andExpect(jsonPath("$.name").value("Test Calculation"));
}

@Test
void shouldBeForbiddenCctCalculationWhenNoToken() throws Exception {
mockMvc.perform(post("/api/cct/calculate"))
.andExpect(status().isForbidden())
.andExpect(jsonPath("$").doesNotExist());
}

@Test
void shouldReturnCctCalculationJsonWhenRequestValid() throws Exception {
String token = TestJwtUtil.generateTokenForTisId(TRAINEE_ID);
mockMvc.perform(post("/api/cct/calculate")
.header(HttpHeaders.AUTHORIZATION, token)
.contentType(MediaType.APPLICATION_JSON)
.content(calculationJson.toString()))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.traineeId").doesNotExist())
.andExpect(jsonPath("$.cctDate").value(LocalDate.MAX.toString()))
.andExpect(jsonPath("$.name").value("Test Calculation"));
}
}
19 changes: 19 additions & 0 deletions src/main/java/uk/nhs/hee/trainee/details/api/CctResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,23 @@ public ResponseEntity<CctCalculationDetailDto> createCalculation(
return ResponseEntity.created(URI.create("/api/cct/calculation/" + savedCalculation.id()))
.body(savedCalculation);
}

/**
* Set the CCT end date in a CCT calculation DTO.
*
* @param calculation The CCT calculation details.
*
* @return The CCT calculation with the CCT end date, or Bad Request if the CCT date could not
* be calculated.
*/
@PostMapping("/calculate")
public ResponseEntity<CctCalculationDetailDto> calculateCctDate(
@RequestBody CctCalculationDetailDto calculation) {
log.info("Request to calculate CCT date [{}]", calculation.name());
Optional<CctCalculationDetailDto> cctDateCalculation = service.calculateCctDate(calculation);
if (cctDateCalculation.isEmpty()) {
return ResponseEntity.badRequest().build();
}
return ResponseEntity.of(cctDateCalculation);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
* @param name A name for the calculation.
* @param programmeMembership The programme membership data for the calculation.
* @param changes The CCT changes to be calculated.
* @param cctDate The calculated CCT end date based on the changes.
* @param created When the calculation was created (auto-generated).
* @param lastModified When the calculation was last modified (auto-generated).
*/
Expand All @@ -65,6 +66,10 @@ public record CctCalculationDetailDto(
@NotEmpty
@Valid
List<CctChangeDto> changes,

@Null(groups = Create.class)
LocalDate cctDate,

Instant created,
Instant lastModified) {

Expand Down
12 changes: 12 additions & 0 deletions src/main/java/uk/nhs/hee/trainee/details/mapper/CctMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import static org.mapstruct.MappingConstants.ComponentModel.SPRING;

import java.time.LocalDate;
import java.util.List;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
Expand Down Expand Up @@ -59,8 +60,19 @@ public interface CctMapper {
* @param entity The entity to convert to a DTO.
* @return The equivalent detail DTO.
*/
@Mapping(target = "cctDate", ignore = true)
CctCalculationDetailDto toDetailDto(CctCalculation entity);

/**
* Insert a cctDate into a {@link CctCalculationDetailDto} DTO.
*
* @param dto The DTO to use.
* @param newCctDate The CCT date to update the DTO with.
* @return The equivalent DTO with CCT date injected.
*/
@Mapping(target = "cctDate", source = "newCctDate")
CctCalculationDetailDto toDetailDto(CctCalculationDetailDto dto, LocalDate newCctDate);

/**
* Convert a {@link CctCalculationDetailDto} DTO to a {@link CctCalculation} entity.
*
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/uk/nhs/hee/trainee/details/service/CctService.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
package uk.nhs.hee.trainee.details.service;

import com.amazonaws.xray.spring.aop.XRayEnabled;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -115,4 +116,16 @@ public CctCalculationDetailDto createCalculation(CctCalculationDetailDto dto) {
log.info("Created CCT calculation [{}] with id [{}]", dto.name(), entity.id());
return mapper.toDetailDto(entity);
}

/**
* Calculate the CCT end date and insert it into a CCT Calculation DTO.
*
* @param dto The CctCalculationDetailDto to use to calculate the CCT end date.
* @return A copy of the DTO with the CCT end date set, or Optional.empty if the calculation was
* not possible due to invalid data.
*/
public Optional<CctCalculationDetailDto> calculateCctDate(CctCalculationDetailDto dto) {
LocalDate cctDate = LocalDate.MAX; //placeholder
return Optional.of(mapper.toDetailDto(dto, cctDate));
}
}
44 changes: 44 additions & 0 deletions src/test/java/uk/nhs/hee/trainee/details/api/CctResourceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.OK;

import java.net.URI;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import org.bson.types.ObjectId;
Expand Down Expand Up @@ -172,4 +174,46 @@ void shouldReturnCreatedCalculationLocation() {
assertThat("Unexpected response code.", headers.getLocation(),
is(URI.create("/api/cct/calculation/" + id)));
}

@Test
void shouldReturnCctCalculation() {
CctCalculationDetailDto dto = CctCalculationDetailDto.builder()
.name("Test Calculation")
.build();

ObjectId id = ObjectId.get();
when(service.calculateCctDate(any())).thenAnswer(inv -> {
CctCalculationDetailDto arg = inv.getArgument(0);
return Optional.of(CctCalculationDetailDto.builder()
.id(id)
.name(arg.name())
.cctDate(LocalDate.MAX)
.build());
});

ResponseEntity<CctCalculationDetailDto> response = controller.calculateCctDate(dto);

assertThat("Unexpected response code.", response.getStatusCode(), is(OK));

CctCalculationDetailDto responseBody = response.getBody();
assertThat("Unexpected response body.", responseBody, notNullValue());
assertThat("Unexpected CCT date.", responseBody.cctDate(), is(LocalDate.MAX));
assertThat("Unexpected name.", responseBody.name(), is("Test Calculation"));
}

@Test
void shouldReturnBadRequestIfErrorInCctCalculation() {
CctCalculationDetailDto dto = CctCalculationDetailDto.builder()
.name("Test Calculation")
.build();

when(service.calculateCctDate(any())).thenReturn(Optional.empty());

ResponseEntity<CctCalculationDetailDto> response = controller.calculateCctDate(dto);

assertThat("Unexpected response code.", response.getStatusCode(), is(BAD_REQUEST));

CctCalculationDetailDto responseBody = response.getBody();
assertThat("Unexpected response body.", responseBody, nullValue());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
Expand Down Expand Up @@ -194,6 +195,7 @@ void shouldGetCalculationWhenBelongsToUser() {
CctCalculationDetailDto dto = result.get();
assertThat("Unexpected calculation ID.", dto.id(), is(calculationId));
assertThat("Unexpected calculation name.", dto.name(), is("Test Calculation"));
assertThat("Unexpected CCT date.", dto.cctDate(), is(nullValue())); //to be fixed
assertThat("Unexpected created timestamp.", dto.created(), is(created));
assertThat("Unexpected last modified timestamp.", dto.lastModified(), is(lastModified));

Expand Down Expand Up @@ -272,6 +274,7 @@ void shouldCreateCalculation() {
CctCalculationDetailDto savedDto = service.createCalculation(dto);
assertThat("Unexpected calculation ID.", savedDto.id(), is(calculationId));
assertThat("Unexpected calculation name.", savedDto.name(), is("Test Calculation"));
assertThat("Unexpected CCT date.", savedDto.cctDate(), is(nullValue()));

CctProgrammeMembershipDto pm = savedDto.programmeMembership();
assertThat("Unexpected PM ID.", pm.id(), is(pmId));
Expand All @@ -293,4 +296,55 @@ void shouldCreateCalculation() {
assertThat("Unexpected change type.", change2.startDate(), is(LocalDate.MAX));
assertThat("Unexpected change type.", change2.wte(), is(0.75));
}

@Test
void shouldCalculateCctDateAndIncludeOtherDetailsInReturnedDto() {
UUID pmId = UUID.randomUUID();

ObjectId calculationId = ObjectId.get();
CctCalculationDetailDto dto = CctCalculationDetailDto.builder()
.id(calculationId)
.name("Test Calculation")
.programmeMembership(CctProgrammeMembershipDto.builder()
.id(pmId)
.name("Test Programme")
.startDate(LocalDate.EPOCH)
.endDate(LocalDate.EPOCH.plusYears(1))
.wte(1.0)
.build())
.changes(List.of(
CctChangeDto.builder().type(LTFT).startDate(LocalDate.MIN).wte(0.5).build(),
CctChangeDto.builder().type(LTFT).startDate(LocalDate.MAX).wte(0.75).build()
))
.build();

Optional<CctCalculationDetailDto> calculatedDtoOptional = service.calculateCctDate(dto);

assertThat("Unexpected CCT calculation.", calculatedDtoOptional.isPresent(), is(true));
CctCalculationDetailDto calculatedDto = calculatedDtoOptional.get();

assertThat("Unexpected calculation ID.", calculatedDto.id(), is(calculationId));
assertThat("Unexpected calculation name.", calculatedDto.name(), is("Test Calculation"));
assertThat("Unexpected calculated CCT date.", calculatedDto.cctDate(), is(LocalDate.MAX));

CctProgrammeMembershipDto pm = calculatedDto.programmeMembership();
assertThat("Unexpected PM ID.", pm.id(), is(pmId));
assertThat("Unexpected PM name.", pm.name(), is("Test Programme"));
assertThat("Unexpected PM start date.", pm.startDate(), is(LocalDate.EPOCH));
assertThat("Unexpected PM end date.", pm.endDate(), is(LocalDate.EPOCH.plusYears(1)));
assertThat("Unexpected PM WTE.", pm.wte(), is(1.0));

List<CctChangeDto> changes = calculatedDto.changes();
assertThat("Unexpected change count.", changes.size(), is(2));

CctChangeDto change1 = changes.get(0);
assertThat("Unexpected change type.", change1.type(), is(LTFT));
assertThat("Unexpected change type.", change1.startDate(), is(LocalDate.MIN));
assertThat("Unexpected change type.", change1.wte(), is(0.5));

CctChangeDto change2 = changes.get(1);
assertThat("Unexpected change type.", change2.type(), is(LTFT));
assertThat("Unexpected change type.", change2.startDate(), is(LocalDate.MAX));
assertThat("Unexpected change type.", change2.wte(), is(0.75));
}
}

0 comments on commit 57b1451

Please sign in to comment.