diff --git a/src/integrationTest/java/uk/gov/hmcts/darts/audio/controller/AudioControllerGetAdminMediaVersionsByIdIntTest.java b/src/integrationTest/java/uk/gov/hmcts/darts/audio/controller/AudioControllerGetAdminMediaVersionsByIdIntTest.java new file mode 100644 index 0000000000..6d698996c5 --- /dev/null +++ b/src/integrationTest/java/uk/gov/hmcts/darts/audio/controller/AudioControllerGetAdminMediaVersionsByIdIntTest.java @@ -0,0 +1,220 @@ +package uk.gov.hmcts.darts.audio.controller; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.skyscreamer.jsonassert.JSONAssert; +import org.skyscreamer.jsonassert.JSONCompareMode; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import uk.gov.hmcts.darts.common.entity.MediaEntity; +import uk.gov.hmcts.darts.common.enums.SecurityRoleEnum; +import uk.gov.hmcts.darts.testutils.GivenBuilder; +import uk.gov.hmcts.darts.testutils.IntegrationBase; +import uk.gov.hmcts.darts.testutils.stubs.DartsDatabaseStub; + +import java.time.Duration; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; + +import static org.junit.jupiter.params.provider.EnumSource.Mode.EXCLUDE; +import static org.junit.jupiter.params.provider.EnumSource.Mode.INCLUDE; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static uk.gov.hmcts.darts.test.common.TestUtils.getContentsFromFile; + +@AutoConfigureMockMvc +class AudioControllerGetAdminMediaVersionsByIdIntTest extends IntegrationBase { + @Autowired + private GivenBuilder given; + + @Autowired + private MockMvc mockMvc; + + @Autowired + private DartsDatabaseStub databaseStub; + + private static final String ENDPOINT_URL = "/admin/medias/{id}/versions"; + private static final String COURTHOUSE_NAME = "TESTCOURTHOUSE"; + private static final String COURTROOM_NAME = "TESTCOURTROOM"; + private static final OffsetDateTime HEARING_START_AT = OffsetDateTime.parse("2024-01-01T12:10:10Z"); + private static final OffsetDateTime MEDIA_START_AT = HEARING_START_AT; + private static final OffsetDateTime MEDIA_END_AT = MEDIA_START_AT.plusHours(1); + private static final String RETAIN_UNTIL = "2200-02-01T00:00:00Z"; + + + @ParameterizedTest + @EnumSource(value = SecurityRoleEnum.class, names = {"SUPER_USER", "SUPER_ADMIN"}, mode = INCLUDE) + void shouldReturn200_whenChronicleIdHasBothCurrentAndNonCurrentVersions(SecurityRoleEnum role) throws Exception { + final String chronicleId = "chronicleId"; + MediaEntity currentMediaEntity = createAndSaveMediaEntity(true, chronicleId, Duration.ofDays(3)); + MediaEntity versionedMediaEntity1 = createAndSaveMediaEntity(false, chronicleId, Duration.ofDays(2)); + MediaEntity versionedMediaEntity2 = createAndSaveMediaEntity(false, chronicleId, Duration.ofDays(1)); + //Craeted unrelated media to ensure not returned + createAndSaveMediaEntity(true, "unrealted"); + + given.anAuthenticatedUserWithGlobalAccessAndRole(role); + + // When + MvcResult mvcResult = mockMvc.perform(get(ENDPOINT_URL, currentMediaEntity.getId())) + .andExpect(status().isOk()) + .andReturn(); + + String expectedResponse = getContentsFromFile( + "tests/audio/AudioControllerGetAdminMediaVersionsByIdIntTest/expectedResponseTypical.json") + .replace("", currentMediaEntity.getCreatedDateTime().format(DateTimeFormatter.ISO_DATE_TIME)) + .replace("", versionedMediaEntity2.getCreatedDateTime().format(DateTimeFormatter.ISO_DATE_TIME)) + .replace("", versionedMediaEntity1.getCreatedDateTime().format(DateTimeFormatter.ISO_DATE_TIME)); + JSONAssert.assertEquals(expectedResponse, mvcResult.getResponse().getContentAsString(), JSONCompareMode.STRICT); + } + + @Test + void shouldReturn200_whenMediaIdPassedIsNotCurrent_shouldReturnTheCorrectCorrentVersion() throws Exception { + final String chronicleId = "chronicleId"; + MediaEntity currentMediaEntity = createAndSaveMediaEntity(true, chronicleId, Duration.ofDays(3)); + MediaEntity versionedMediaEntity1 = createAndSaveMediaEntity(false, chronicleId, Duration.ofDays(2)); + MediaEntity versionedMediaEntity2 = createAndSaveMediaEntity(false, chronicleId, Duration.ofDays(1)); + //Craeted unrelated media to ensure not returned + createAndSaveMediaEntity(true, "unrealted"); + + given.anAuthenticatedUserWithGlobalAccessAndRole(SecurityRoleEnum.SUPER_ADMIN); + + // When + MvcResult mvcResult = mockMvc.perform(get(ENDPOINT_URL, versionedMediaEntity1.getId())) + .andExpect(status().isOk()) + .andReturn(); + + String expectedResponse = getContentsFromFile( + "tests/audio/AudioControllerGetAdminMediaVersionsByIdIntTest/expectedResponseTypical.json") + .replace("", currentMediaEntity.getCreatedDateTime().format(DateTimeFormatter.ISO_DATE_TIME)) + .replace("", versionedMediaEntity2.getCreatedDateTime().format(DateTimeFormatter.ISO_DATE_TIME)) + .replace("", versionedMediaEntity1.getCreatedDateTime().format(DateTimeFormatter.ISO_DATE_TIME)); + JSONAssert.assertEquals(expectedResponse, mvcResult.getResponse().getContentAsString(), JSONCompareMode.STRICT); + } + + @Test + void shouldReturn200_whenChronicleIdHasOnlyCurrentAndNoVersions() throws Exception { + final String chronicleId = "chronicleId"; + MediaEntity currentMediaEntity = createAndSaveMediaEntity(true, chronicleId, Duration.ofDays(3)); + //Craeted unrelated media to ensure not returned + createAndSaveMediaEntity(true, "unrealted"); + + given.anAuthenticatedUserWithGlobalAccessAndRole(SecurityRoleEnum.SUPER_ADMIN); + + // When + MvcResult mvcResult = mockMvc.perform(get(ENDPOINT_URL, currentMediaEntity.getId())) + .andExpect(status().isOk()) + .andReturn(); + + String expectedResponse = getContentsFromFile( + "tests/audio/AudioControllerGetAdminMediaVersionsByIdIntTest/expectedResponseNoVersions.json") + .replace("", currentMediaEntity.getCreatedDateTime().format(DateTimeFormatter.ISO_DATE_TIME)); + JSONAssert.assertEquals(expectedResponse, mvcResult.getResponse().getContentAsString(), JSONCompareMode.STRICT); + } + + @ParameterizedTest + @EnumSource(value = SecurityRoleEnum.class, names = {"SUPER_USER", "SUPER_ADMIN"}, mode = INCLUDE) + void shouldReturn404_WhenMediaRecordDoesNotExist(SecurityRoleEnum role) throws Exception { + // Given + given.anAuthenticatedUserWithGlobalAccessAndRole(role); + + // When + MvcResult mvcResult = mockMvc.perform(get(ENDPOINT_URL, "123456789")) + .andExpect(status().isNotFound()) + .andReturn(); + + // Then + var jsonString = mvcResult.getResponse().getContentAsString(); + JSONAssert.assertEquals(""" + { + "type": "AUDIO_102", + "title": "The requested media cannot be found", + "status": 404 + } + """, jsonString, JSONCompareMode.STRICT); + } + + @ParameterizedTest + @EnumSource(value = SecurityRoleEnum.class, names = {"SUPER_USER", "SUPER_ADMIN"}, mode = INCLUDE) + void shouldReturn404_WhenMediaRecordIsDeleted(SecurityRoleEnum role) throws Exception { + // Given + given.anAuthenticatedUserWithGlobalAccessAndRole(role); + + + var mediaEntity = createAndSaveMediaEntity(true, true, "chronicleId", Duration.ofMillis(0)); + // When + MvcResult mvcResult = mockMvc.perform(get(ENDPOINT_URL, String.valueOf(mediaEntity.getId()))) + .andExpect(status().isNotFound()) + .andReturn(); + + // Then + var jsonString = mvcResult.getResponse().getContentAsString(); + JSONAssert.assertEquals(""" + { + "type": "AUDIO_102", + "title": "The requested media cannot be found", + "status": 404 + } + """, jsonString, JSONCompareMode.STRICT); + + } + + @ParameterizedTest + @EnumSource(value = SecurityRoleEnum.class, names = {"SUPER_USER", "SUPER_ADMIN"}, mode = EXCLUDE) + void shouldDenyAccess_whenNotAuthorised(SecurityRoleEnum role) throws Exception { + // Given + given.anAuthenticatedUserWithGlobalAccessAndRole(role); + + // When + MvcResult mvcResult = mockMvc.perform(get(ENDPOINT_URL, "123456789")) + .andExpect(status().isForbidden()) + .andReturn(); + + // Then + var jsonString = mvcResult.getResponse().getContentAsString(); + JSONAssert.assertEquals(""" + { + "type": "AUTHORISATION_109", + "title": "User is not authorised for this endpoint", + "status": 403 + } + """, jsonString, JSONCompareMode.STRICT); + } + + private MediaEntity createAndSaveMediaEntity(boolean isCurrent, String chronicleId) { + return createAndSaveMediaEntity(false, isCurrent, chronicleId, Duration.ofMillis(0)); + } + + private MediaEntity createAndSaveMediaEntity(boolean isCurrent, String chronicleId, Duration timeOffset) { + return createAndSaveMediaEntity(false, isCurrent, chronicleId, timeOffset); + } + + private MediaEntity createAndSaveMediaEntity(boolean isDeleted, boolean isCurrent, String chronicleId, Duration timeOffset) { + MediaEntity mediaEntity = databaseStub.createMediaEntity(COURTHOUSE_NAME, + COURTROOM_NAME, + MEDIA_START_AT.plus(timeOffset), + MEDIA_END_AT.plus(timeOffset), + 2); + var userAccountEntity = databaseStub.getUserAccountRepository().findAll().stream() + .findFirst() + .orElseThrow(); + mediaEntity.setLegacyObjectId("object-id-value"); + mediaEntity.setContentObjectId("content-id-value"); + mediaEntity.setClipId("clip-id-value"); + mediaEntity.setMediaStatus("media-status-value"); + mediaEntity.setHidden(true); + mediaEntity.setDeleted(isDeleted); + mediaEntity.setLegacyVersionLabel("version-label-value"); + mediaEntity.setChronicleId(chronicleId); + mediaEntity.setAntecedentId("antecedent-value"); + mediaEntity.setRetainUntilTs(OffsetDateTime.parse(RETAIN_UNTIL)); + mediaEntity.setCreatedBy(userAccountEntity); + mediaEntity.setLastModifiedBy(userAccountEntity); + mediaEntity.setIsCurrent(isCurrent); + + return databaseStub.getMediaRepository() + .saveAndFlush(mediaEntity); + } +} diff --git a/src/integrationTest/resources/tests/audio/AudioControllerGetAdminMediaVersionsByIdIntTest/expectedResponseNoVersions.json b/src/integrationTest/resources/tests/audio/AudioControllerGetAdminMediaVersionsByIdIntTest/expectedResponseNoVersions.json new file mode 100644 index 0000000000..ca45beb8ad --- /dev/null +++ b/src/integrationTest/resources/tests/audio/AudioControllerGetAdminMediaVersionsByIdIntTest/expectedResponseNoVersions.json @@ -0,0 +1,22 @@ +{ + "media_object_id": "object-id-value", + "current_version": { + "id": 1, + "courthouse": { + "id": 1, + "display_name": "TESTCOURTHOUSE" + }, + "courtroom": { + "id": 1, + "name": "TESTCOURTROOM" + }, + "start_at": "2024-01-04T12:10:10Z", + "end_at": "2024-01-04T13:10:10Z", + "channel": 2, + "chronicle_id": "chronicleId", + "antecedent_id": "antecedent-value", + "is_current": true, + "created_at": "" + }, + "previous_versions": [] +} \ No newline at end of file diff --git a/src/integrationTest/resources/tests/audio/AudioControllerGetAdminMediaVersionsByIdIntTest/expectedResponseTypical.json b/src/integrationTest/resources/tests/audio/AudioControllerGetAdminMediaVersionsByIdIntTest/expectedResponseTypical.json new file mode 100644 index 0000000000..7883035ef8 --- /dev/null +++ b/src/integrationTest/resources/tests/audio/AudioControllerGetAdminMediaVersionsByIdIntTest/expectedResponseTypical.json @@ -0,0 +1,59 @@ +{ + "media_object_id": "object-id-value", + "current_version": { + "id": 1, + "courthouse": { + "id": 1, + "display_name": "TESTCOURTHOUSE" + }, + "courtroom": { + "id": 1, + "name": "TESTCOURTROOM" + }, + "start_at": "2024-01-04T12:10:10Z", + "end_at": "2024-01-04T13:10:10Z", + "channel": 2, + "chronicle_id": "chronicleId", + "antecedent_id": "antecedent-value", + "is_current": true, + "created_at": "" + }, + "previous_versions": [ + { + "id": 3, + "courthouse": { + "id": 1, + "display_name": "TESTCOURTHOUSE" + }, + "courtroom": { + "id": 1, + "name": "TESTCOURTROOM" + }, + "start_at": "2024-01-02T12:10:10Z", + "end_at": "2024-01-02T13:10:10Z", + "channel": 2, + "chronicle_id": "chronicleId", + "antecedent_id": "antecedent-value", + "is_current": false, + "created_at": "" + }, + { + "id": 2, + "courthouse": { + "id": 1, + "display_name": "TESTCOURTHOUSE" + }, + "courtroom": { + "id": 1, + "name": "TESTCOURTROOM" + }, + "start_at": "2024-01-03T12:10:10Z", + "end_at": "2024-01-03T13:10:10Z", + "channel": 2, + "chronicle_id": "chronicleId", + "antecedent_id": "antecedent-value", + "is_current": false, + "created_at": "" + } + ] +} \ No newline at end of file diff --git a/src/main/java/uk/gov/hmcts/darts/audio/controller/AudioController.java b/src/main/java/uk/gov/hmcts/darts/audio/controller/AudioController.java index 0fab69eecc..18c43962dd 100644 --- a/src/main/java/uk/gov/hmcts/darts/audio/controller/AudioController.java +++ b/src/main/java/uk/gov/hmcts/darts/audio/controller/AudioController.java @@ -17,6 +17,7 @@ import uk.gov.hmcts.darts.audio.mapper.TransformedMediaMapper; import uk.gov.hmcts.darts.audio.model.AddAudioMetadataRequestWithStorageGUID; import uk.gov.hmcts.darts.audio.model.AdminMediaResponse; +import uk.gov.hmcts.darts.audio.model.AdminVersionedMediaResponse; import uk.gov.hmcts.darts.audio.model.AudioMetadata; import uk.gov.hmcts.darts.audio.model.AudioPreview; import uk.gov.hmcts.darts.audio.model.GetAdminMediaResponseItem; @@ -164,6 +165,13 @@ public ResponseEntity getAdminMediasById(Integer id) { return new ResponseEntity<>(adminMediaService.getMediasById(id), HttpStatus.OK); } + @Override + @SecurityRequirement(name = SECURITY_SCHEMES_BEARER_AUTH) + @Authorisation(contextId = ANY_ENTITY_ID, globalAccessSecurityRoles = {SUPER_USER, SUPER_ADMIN}) + public ResponseEntity getAdminMediaVersionsById(Integer id) { + return new ResponseEntity<>(adminMediaService.getMediaVersionsById(id), HttpStatus.OK); + } + @Override @SecurityRequirement(name = SECURITY_SCHEMES_BEARER_AUTH) @Authorisation(contextId = ANY_ENTITY_ID, globalAccessSecurityRoles = {SUPER_USER, SUPER_ADMIN}) diff --git a/src/main/java/uk/gov/hmcts/darts/audio/mapper/GetAdminMediaResponseMapper.java b/src/main/java/uk/gov/hmcts/darts/audio/mapper/GetAdminMediaResponseMapper.java index bee6e750cd..91baa53d58 100644 --- a/src/main/java/uk/gov/hmcts/darts/audio/mapper/GetAdminMediaResponseMapper.java +++ b/src/main/java/uk/gov/hmcts/darts/audio/mapper/GetAdminMediaResponseMapper.java @@ -1,8 +1,12 @@ package uk.gov.hmcts.darts.audio.mapper; -import lombok.experimental.UtilityClass; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import uk.gov.hmcts.darts.audio.model.AdminActionResponse; +import uk.gov.hmcts.darts.audio.model.AdminMediaVersionResponse; +import uk.gov.hmcts.darts.audio.model.AdminVersionedMediaResponse; import uk.gov.hmcts.darts.audio.model.GetAdminMediaResponseCase; import uk.gov.hmcts.darts.audio.model.GetAdminMediaResponseCourthouse; import uk.gov.hmcts.darts.audio.model.GetAdminMediaResponseCourtroom; @@ -19,13 +23,19 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.Optional; @Slf4j -@UtilityClass +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class GetAdminMediaResponseMapper { - public List createResponseItemList(List mediaEntities, HearingEntity hearing) { + private final CourtroomMapper courtroomMapper; + private final CourthouseMapper courthouseMapper; + + public static List createResponseItemList(List mediaEntities, HearingEntity hearing) { List responseList = new ArrayList<>(); for (MediaEntity mediaEntity : mediaEntities) { responseList.add(createResponseItem(mediaEntity, hearing)); @@ -33,7 +43,7 @@ public List createResponseItemList(List return responseList; } - public GetAdminMediaResponseItem createResponseItem(MediaEntity mediaEntity, HearingEntity hearing) { + public static GetAdminMediaResponseItem createResponseItem(MediaEntity mediaEntity, HearingEntity hearing) { GetAdminMediaResponseItem responseItem = new GetAdminMediaResponseItem(); responseItem.setId(mediaEntity.getId()); responseItem.setChannel(mediaEntity.getChannel()); @@ -48,21 +58,21 @@ public GetAdminMediaResponseItem createResponseItem(MediaEntity mediaEntity, Hea return responseItem; } - private GetAdminMediaResponseCase createResponseCase(CourtCaseEntity courtCaseEntity) { + private static GetAdminMediaResponseCase createResponseCase(CourtCaseEntity courtCaseEntity) { GetAdminMediaResponseCase responseCase = new GetAdminMediaResponseCase(); responseCase.setId(courtCaseEntity.getId()); responseCase.setCaseNumber(courtCaseEntity.getCaseNumber()); return responseCase; } - private GetAdminMediaResponseHearing createResponseHearing(HearingEntity hearingEntity) { + private static GetAdminMediaResponseHearing createResponseHearing(HearingEntity hearingEntity) { GetAdminMediaResponseHearing responseHearing = new GetAdminMediaResponseHearing(); responseHearing.setId(hearingEntity.getId()); responseHearing.setHearingDate(hearingEntity.getHearingDate()); return responseHearing; } - private GetAdminMediaResponseCourthouse createResponseCourthouse(HearingEntity hearingEntity) { + private static GetAdminMediaResponseCourthouse createResponseCourthouse(HearingEntity hearingEntity) { CourthouseEntity courthouse = hearingEntity.getCourtroom().getCourthouse(); GetAdminMediaResponseCourthouse responseCourthouse = new GetAdminMediaResponseCourthouse(); responseCourthouse.setId(courthouse.getId()); @@ -70,7 +80,7 @@ private GetAdminMediaResponseCourthouse createResponseCourthouse(HearingEntity h return responseCourthouse; } - private GetAdminMediaResponseCourtroom createResponseCourtroom(HearingEntity hearingEntity) { + private static GetAdminMediaResponseCourtroom createResponseCourtroom(HearingEntity hearingEntity) { CourtroomEntity courtroom = hearingEntity.getCourtroom(); GetAdminMediaResponseCourtroom responseCourthouse = new GetAdminMediaResponseCourtroom(); responseCourthouse.setId(courtroom.getId()); @@ -78,7 +88,7 @@ private GetAdminMediaResponseCourtroom createResponseCourtroom(HearingEntity hea return responseCourthouse; } - public MediaHideResponse mapHideOrShowResponse(MediaEntity entity, ObjectAdminActionEntity objectAdminActionEntity) { + public static MediaHideResponse mapHideOrShowResponse(MediaEntity entity, ObjectAdminActionEntity objectAdminActionEntity) { MediaHideResponse response = new MediaHideResponse(); response.setId(entity.getId()); response.setIsHidden(entity.isHidden()); @@ -107,7 +117,8 @@ private static AdminActionResponse buildAdminActionResponse(ObjectAdminActionEnt return aaResponse; } - public MediaApproveMarkedForDeletionResponse mapMediaApproveMarkedForDeletionResponse(MediaEntity entity, ObjectAdminActionEntity objectAdminActionEntity) { + public static MediaApproveMarkedForDeletionResponse mapMediaApproveMarkedForDeletionResponse(MediaEntity entity, + ObjectAdminActionEntity objectAdminActionEntity) { MediaApproveMarkedForDeletionResponse response = new MediaApproveMarkedForDeletionResponse(); response.setId(entity.getId()); response.setIsHidden(entity.isHidden()); @@ -119,4 +130,38 @@ public MediaApproveMarkedForDeletionResponse mapMediaApproveMarkedForDeletionRes return response; } + + public AdminVersionedMediaResponse mapAdminVersionedMediaResponse(MediaEntity mediaEntity, List mediaVersions) { + AdminVersionedMediaResponse response = new AdminVersionedMediaResponse(); + response.setMediaObjectId(mediaEntity.getLegacyObjectId()); + response.setCurrentVersion(mapAdminMediaVersionResponse(mediaEntity)); + response.setPreviousVersions( + mediaVersions.stream() + .map(this::mapAdminMediaVersionResponse) + .filter(Objects::nonNull) + .toList() + ); + return response; + } + + AdminMediaVersionResponse mapAdminMediaVersionResponse(MediaEntity mediaEntity) { + if (mediaEntity == null) { + return null; + } + AdminMediaVersionResponse response = new AdminMediaVersionResponse(); + response.setId(mediaEntity.getId()); + response.setCourtroom(courtroomMapper.toApiModel(mediaEntity.getCourtroom())); + response.setCourthouse(courthouseMapper.toApiModel( + Optional.ofNullable(mediaEntity.getCourtroom()) + .map(courtroomEntity -> courtroomEntity.getCourthouse()) + .orElse(null))); + response.setStartAt(mediaEntity.getStart()); + response.setEndAt(mediaEntity.getEnd()); + response.setChannel(mediaEntity.getChannel()); + response.setChronicleId(mediaEntity.getChronicleId()); + response.setAntecedentId(mediaEntity.getAntecedentId()); + response.setIsCurrent(mediaEntity.getIsCurrent()); + response.setCreatedAt(mediaEntity.getCreatedDateTime()); + return response; + } } \ No newline at end of file diff --git a/src/main/java/uk/gov/hmcts/darts/audio/service/AdminMediaService.java b/src/main/java/uk/gov/hmcts/darts/audio/service/AdminMediaService.java index 13ba0e2afa..54b8c78179 100644 --- a/src/main/java/uk/gov/hmcts/darts/audio/service/AdminMediaService.java +++ b/src/main/java/uk/gov/hmcts/darts/audio/service/AdminMediaService.java @@ -1,6 +1,7 @@ package uk.gov.hmcts.darts.audio.service; import uk.gov.hmcts.darts.audio.model.AdminMediaResponse; +import uk.gov.hmcts.darts.audio.model.AdminVersionedMediaResponse; import uk.gov.hmcts.darts.audio.model.GetAdminMediaResponseItem; import uk.gov.hmcts.darts.audio.model.GetAdminMediasMarkedForDeletionItem; import uk.gov.hmcts.darts.audio.model.MediaApproveMarkedForDeletionResponse; @@ -30,4 +31,6 @@ default List filterMediasWithTransformedMediaId(Integ List getMediasMarkedForDeletion(); MediaApproveMarkedForDeletionResponse adminApproveMediaMarkedForDeletion(Integer mediaId); + + AdminVersionedMediaResponse getMediaVersionsById(Integer id); } \ No newline at end of file diff --git a/src/main/java/uk/gov/hmcts/darts/audio/service/impl/AdminMediaServiceImpl.java b/src/main/java/uk/gov/hmcts/darts/audio/service/impl/AdminMediaServiceImpl.java index cdbba18915..91db364879 100644 --- a/src/main/java/uk/gov/hmcts/darts/audio/service/impl/AdminMediaServiceImpl.java +++ b/src/main/java/uk/gov/hmcts/darts/audio/service/impl/AdminMediaServiceImpl.java @@ -3,6 +3,7 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -21,6 +22,7 @@ import uk.gov.hmcts.darts.audio.mapper.PostAdminMediaSearchResponseMapper; import uk.gov.hmcts.darts.audio.model.AdminActionRequest; import uk.gov.hmcts.darts.audio.model.AdminMediaResponse; +import uk.gov.hmcts.darts.audio.model.AdminVersionedMediaResponse; import uk.gov.hmcts.darts.audio.model.GetAdminMediaResponseItem; import uk.gov.hmcts.darts.audio.model.GetAdminMediasMarkedForDeletionAdminAction; import uk.gov.hmcts.darts.audio.model.GetAdminMediasMarkedForDeletionItem; @@ -43,6 +45,7 @@ import uk.gov.hmcts.darts.common.entity.ObjectAdminActionEntity; import uk.gov.hmcts.darts.common.entity.ObjectHiddenReasonEntity; import uk.gov.hmcts.darts.common.entity.TransformedMediaEntity; +import uk.gov.hmcts.darts.common.entity.base.CreatedBaseEntity; import uk.gov.hmcts.darts.common.exception.CommonApiError; import uk.gov.hmcts.darts.common.exception.DartsApiException; import uk.gov.hmcts.darts.common.helper.CurrentTimeHelper; @@ -54,6 +57,7 @@ import java.time.OffsetDateTime; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -62,6 +66,7 @@ @Service @RequiredArgsConstructor +@Slf4j public class AdminMediaServiceImpl implements AdminMediaService { private final SearchMediaValidator searchMediaValidator; @@ -79,6 +84,7 @@ public class AdminMediaServiceImpl implements AdminMediaService { private final CourthouseMapper courthouseMapper; private final CourtroomMapper courtroomMapper; private final ObjectActionMapper objectActionMapper; + private final GetAdminMediaResponseMapper getAdminMediaResponseMapper; private final MediaRepository mediaRepository; private final TransformedMediaRepository transformedMediaRepository; @@ -95,8 +101,7 @@ public class AdminMediaServiceImpl implements AdminMediaService { @Override public AdminMediaResponse getMediasById(Integer id) { - var mediaEntity = mediaRepository.findById(id) - .orElseThrow(() -> new DartsApiException(AudioApiError.MEDIA_NOT_FOUND)); + var mediaEntity = getMediaEntityById(id); AdminMediaResponse adminMediaResponse = adminMediaMapper.toApiModel(mediaEntity); adminMediaResponse.getCases().sort((o1, o2) -> o2.getCaseNumber().compareTo(o1.getCaseNumber())); @@ -104,6 +109,11 @@ public AdminMediaResponse getMediasById(Integer id) { return adminMediaResponse; } + MediaEntity getMediaEntityById(Integer id) { + return mediaRepository.findById(id) + .orElseThrow(() -> new DartsApiException(AudioApiError.MEDIA_NOT_FOUND)); + } + @Override public List performAdminMediasSearchPost(PostAdminMediasSearchRequest adminMediasSearchRequest) { List matchingMedia = postAdminMediasSearchHelper.getMatchingMedia(adminMediasSearchRequest); @@ -262,7 +272,48 @@ public MediaApproveMarkedForDeletionResponse adminApproveMediaMarkedForDeletion( auditApi.record(AuditActivity.MANUAL_DELETION, currentUser, objectAdminActionEntity.getId().toString()); - return GetAdminMediaResponseMapper.mapMediaApproveMarkedForDeletionResponse(mediaEntity, objectAdminActionEntity); + return getAdminMediaResponseMapper.mapMediaApproveMarkedForDeletionResponse(mediaEntity, objectAdminActionEntity); + } + + @Override + public AdminVersionedMediaResponse getMediaVersionsById(Integer id) { + MediaEntity mediaEntityFromRequest = getMediaEntityById(id); + + if (mediaEntityFromRequest.getChronicleId() == null) { + throw new DartsApiException(CommonApiError.INTERNAL_SERVER_ERROR, + "Media " + id + " has a Chronicle Id that is null. As such we can not ensure accurate results are returned"); + } + List mediaVersions = mediaRepository.findAllByChronicleId(mediaEntityFromRequest.getChronicleId()); + + + List currentMediaVersions = mediaVersions.stream() + .filter(mediaEntity -> mediaEntity.getIsCurrent() != null) + .filter(media -> media.getIsCurrent()) + .sorted(Comparator.comparing(CreatedBaseEntity::getCreatedDateTime)) + .collect(Collectors.toCollection(ArrayList::new)); + + List versionedMedia = mediaVersions.stream() + .filter(media -> media.getIsCurrent() == null || !media.getIsCurrent()) + .sorted(Comparator.comparing(CreatedBaseEntity::getCreatedDateTime).reversed()) + .collect(Collectors.toCollection(ArrayList::new)); + + MediaEntity currentVersion; + if (currentMediaVersions.size() == 1) { + currentVersion = currentMediaVersions.getLast(); + } else if (currentMediaVersions.isEmpty()) { + currentVersion = null; + log.info("Media with id {} has no current versions", id); + } else { + log.warn("Media with id {} has {} current versions we only expect one", id, currentMediaVersions.size()); + currentVersion = currentMediaVersions.getLast(); + //Add any extra current events to top of versionedMedia so they still get displayed + currentMediaVersions.removeLast(); + currentMediaVersions + .forEach(mediaEntity -> { + versionedMedia.addFirst(mediaEntity); + }); + } + return getAdminMediaResponseMapper.mapAdminVersionedMediaResponse(currentVersion, versionedMedia); } private ApplyAdminActionComponent.AdminActionProperties mapToAdminActionProperties(AdminActionRequest adminActionRequest) { diff --git a/src/main/java/uk/gov/hmcts/darts/common/exception/CommonApiError.java b/src/main/java/uk/gov/hmcts/darts/common/exception/CommonApiError.java index f7fa6e330f..4bdc1fa823 100644 --- a/src/main/java/uk/gov/hmcts/darts/common/exception/CommonApiError.java +++ b/src/main/java/uk/gov/hmcts/darts/common/exception/CommonApiError.java @@ -24,6 +24,11 @@ public enum CommonApiError implements DartsApiError { CommonErrorCode.NOT_FOUND.getValue(), HttpStatus.NOT_FOUND, CommonTitleErrors.NOT_FOUND.getValue() + ), + INTERNAL_SERVER_ERROR( + CommonErrorCode.INTERNAL_SERVER_ERROR.getValue(), + HttpStatus.INTERNAL_SERVER_ERROR, + CommonTitleErrors.INTERNAL_SERVER_ERROR.getValue() ); private static final String ERROR_TYPE_PREFIX = "COMMON"; diff --git a/src/main/resources/openapi/audio.yaml b/src/main/resources/openapi/audio.yaml index 8f55d2821a..8559284ede 100644 --- a/src/main/resources/openapi/audio.yaml +++ b/src/main/resources/openapi/audio.yaml @@ -276,7 +276,6 @@ paths: type: "AUTHORISATION_100" title: "User is not authorised for the associated courthouse" status: 401 - /admin/medias/{id}: get: tags: @@ -296,7 +295,41 @@ paths: application/json: schema: $ref: '#/components/schemas/AdminMediaResponse' - + /admin/medias/{id}/versions: + get: + tags: + - Audio + operationId: getAdminMediaVersionsById + description: Returns a representation of a media record and associated versions + parameters: + - in: path + name: id + required: true + schema: + $ref: '#/components/schemas/MediaId' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/AdminVersionedMediaResponse' + '401': + description: Unauthorised Error + '403': + description: Forbidden Error + content: + application/json+problem: + schema: + allOf: + - $ref: './problem.yaml' + '404': + description: Not Found Error + content: + application/json+problem: + schema: + allOf: + - $ref: './problem.yaml' /admin/medias/{media_id}/hide: post: tags: @@ -567,6 +600,40 @@ components: $ref: '#/components/schemas/AdminMediaCourtroomResponse' admin_action: $ref: '#/components/schemas/AdminActionResponse' + AdminVersionedMediaResponse: + type: object + properties: + media_object_id: + type: string + current_version: + $ref: '#/components/schemas/AdminMediaVersionResponse' + previous_versions: + type: array + items: + $ref: '#/components/schemas/AdminMediaVersionResponse' + AdminMediaVersionResponse: + type: object + properties: + id: + $ref: '#/components/schemas/MediaId' + courthouse: + $ref: '#/components/schemas/AdminMediaCourthouseResponse' + courtroom: + $ref: '#/components/schemas/AdminMediaCourtroomResponse' + start_at: + $ref: '#/components/schemas/StartAt' + end_at: + $ref: '#/components/schemas/EndAt' + channel: + $ref: '#/components/schemas/Channel' + chronicle_id: + $ref: '#/components/schemas/ChronicleId' + antecedent_id: + $ref: '#/components/schemas/AntecedentId' + is_current: + $ref: '#/components/schemas/IsCurrent' + created_at: + $ref: '#/components/schemas/CreatedAt' AdminMediaResponse: type: object properties: diff --git a/src/main/resources/openapi/common.yaml b/src/main/resources/openapi/common.yaml index 99e3ac1ad4..6de458c48d 100644 --- a/src/main/resources/openapi/common.yaml +++ b/src/main/resources/openapi/common.yaml @@ -174,9 +174,10 @@ components: - "COMMON_100" - "COMMON_101" - "COMMON_102" + - "COMMON_103" x-enum-varnames: [ COURTHOUSE_PROVIDED_DOES_NOT_EXIST, FEATURE_FLAG_NOT_ENABLED, - NOT_FOUND ] + NOT_FOUND, INTERNAL_SERVER_ERROR ] CommonTitleErrors: type: string @@ -184,7 +185,8 @@ components: - "Provided courthouse does not exist" - "Feature flag not enabled" - "Resource not found" + - "Internal server error" x-enum-varnames: [ COURTHOUSE_PROVIDED_DOES_NOT_EXIST , FEATURE_FLAG_NOT_ENABLED, - NOT_FOUND + NOT_FOUND, INTERNAL_SERVER_ERROR ] \ No newline at end of file diff --git a/src/test/java/uk/gov/hmcts/darts/audio/mapper/GetAdminMediaResponseMapperTest.java b/src/test/java/uk/gov/hmcts/darts/audio/mapper/GetAdminMediaResponseMapperTest.java new file mode 100644 index 0000000000..4a5ac6a765 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/darts/audio/mapper/GetAdminMediaResponseMapperTest.java @@ -0,0 +1,147 @@ +package uk.gov.hmcts.darts.audio.mapper; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.hmcts.darts.audio.model.AdminMediaCourthouseResponse; +import uk.gov.hmcts.darts.audio.model.AdminMediaCourtroomResponse; +import uk.gov.hmcts.darts.audio.model.AdminMediaVersionResponse; +import uk.gov.hmcts.darts.audio.model.AdminVersionedMediaResponse; +import uk.gov.hmcts.darts.common.entity.CourthouseEntity; +import uk.gov.hmcts.darts.common.entity.CourtroomEntity; +import uk.gov.hmcts.darts.common.entity.MediaEntity; + +import java.time.OffsetDateTime; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class GetAdminMediaResponseMapperTest { + + + @Mock + private CourtroomMapper courtroomMapper; + @Mock + private CourthouseMapper courthouseMapper; + + @InjectMocks + @Spy + private GetAdminMediaResponseMapper getAdminMediaResponseMapper; + + @Test + void mapAdminVersionedMediaResponse_shouldMapAdminVersionedMediaResponse() { + final String legacyObjectId = "legacyObjectId"; + MediaEntity mediaEntity = mock(MediaEntity.class); + when(mediaEntity.getLegacyObjectId()).thenReturn(legacyObjectId); + + MediaEntity versioendMediaEntity1 = mock(MediaEntity.class); + MediaEntity versioendMediaEntity2 = mock(MediaEntity.class); + MediaEntity versioendMediaEntity3 = mock(MediaEntity.class); + List versionedMediaEntities = List.of(versioendMediaEntity1, versioendMediaEntity2, versioendMediaEntity3); + + AdminMediaVersionResponse mediaEntityVersionedResponse = mock(AdminMediaVersionResponse.class); + AdminMediaVersionResponse versionedMediaEntityVersionedResponse1 = mock(AdminMediaVersionResponse.class); + AdminMediaVersionResponse versionedMediaEntityVersionedResponse3 = mock(AdminMediaVersionResponse.class); + + doReturn(mediaEntityVersionedResponse).when(getAdminMediaResponseMapper).mapAdminMediaVersionResponse(mediaEntity); + doReturn(versionedMediaEntityVersionedResponse1).when(getAdminMediaResponseMapper).mapAdminMediaVersionResponse(versioendMediaEntity1); + doReturn(null).when(getAdminMediaResponseMapper).mapAdminMediaVersionResponse(versioendMediaEntity2); + doReturn(versionedMediaEntityVersionedResponse3).when(getAdminMediaResponseMapper).mapAdminMediaVersionResponse(versioendMediaEntity3); + + + AdminVersionedMediaResponse adminVersionedMediaResponse = getAdminMediaResponseMapper.mapAdminVersionedMediaResponse(mediaEntity, + versionedMediaEntities); + assertThat(adminVersionedMediaResponse).isNotNull(); + assertThat(adminVersionedMediaResponse.getMediaObjectId()).isEqualTo(legacyObjectId); + assertThat(adminVersionedMediaResponse.getCurrentVersion()).isEqualTo(mediaEntityVersionedResponse); + //versionedMediaEntityVersionedResponse2 should be excluded as it is null + assertThat(adminVersionedMediaResponse.getPreviousVersions()) + .containsExactly(versionedMediaEntityVersionedResponse1, + versionedMediaEntityVersionedResponse3); + } + + @Test + void mapAdminMediaVersionResponse_courtRoomDoesNotExist_shouldMapAdminMediaVersionResponse() { + MediaEntity media = new MediaEntity(); + media.setId(321); + media.setStart(OffsetDateTime.now()); + media.setEnd(OffsetDateTime.now().plusDays(1)); + media.setChannel(2); + media.setLegacyObjectId("legacyObjectId2"); + media.setChronicleId("chronicleId2"); + media.setAntecedentId("antecedentId2"); + media.setIsCurrent(false); + media.setCreatedDateTime(OffsetDateTime.now().plusDays(3)); + + AdminMediaVersionResponse response = getAdminMediaResponseMapper.mapAdminMediaVersionResponse(media); + + assertThat(response).isNotNull(); + assertThat(response.getId()).isEqualTo(media.getId()); + assertThat(response.getCourtroom()).isNull(); + assertThat(response.getCourthouse()).isNull(); + assertThat(response.getStartAt()).isEqualTo(media.getStart()); + assertThat(response.getEndAt()).isEqualTo(media.getEnd()); + + assertThat(response.getChannel()).isEqualTo(media.getChannel()); + assertThat(response.getChronicleId()).isEqualTo(media.getChronicleId()); + assertThat(response.getAntecedentId()).isEqualTo(media.getAntecedentId()); + assertThat(response.getIsCurrent()).isEqualTo(media.getIsCurrent()); + assertThat(response.getCreatedAt()).isEqualTo(media.getCreatedDateTime()); + } + + @Test + void mapAdminMediaVersionResponse_shouldMapAdminMediaVersionResponse() { + CourtroomEntity courtroomEntity = mock(CourtroomEntity.class); + AdminMediaCourtroomResponse courtroom = mock(AdminMediaCourtroomResponse.class); + when(courtroomMapper.toApiModel(courtroomEntity)).thenReturn(courtroom); + + CourthouseEntity courthouseEntity = mock(CourthouseEntity.class); + AdminMediaCourthouseResponse courthouse = mock(AdminMediaCourthouseResponse.class); + when(courthouseMapper.toApiModel(courthouseEntity)).thenReturn(courthouse); + when(courtroomEntity.getCourthouse()).thenReturn(courthouseEntity); + + MediaEntity media = new MediaEntity(); + media.setId(123); + media.setCourtroom(courtroomEntity); + media.setStart(OffsetDateTime.now()); + media.setEnd(OffsetDateTime.now().plusDays(1)); + media.setChannel(1); + media.setLegacyObjectId("legacyObjectId"); + media.setChronicleId("chronicleId"); + media.setAntecedentId("antecedentId"); + media.setIsCurrent(true); + media.setCreatedDateTime(OffsetDateTime.now().plusDays(3)); + + AdminMediaVersionResponse response = getAdminMediaResponseMapper.mapAdminMediaVersionResponse(media); + + assertThat(response).isNotNull(); + assertThat(response.getId()).isEqualTo(media.getId()); + assertThat(response.getCourtroom()).isEqualTo(courtroom); + assertThat(response.getCourthouse()).isEqualTo(courthouse); + assertThat(response.getStartAt()).isEqualTo(media.getStart()); + assertThat(response.getEndAt()).isEqualTo(media.getEnd()); + + assertThat(response.getChannel()).isEqualTo(media.getChannel()); + assertThat(response.getChronicleId()).isEqualTo(media.getChronicleId()); + assertThat(response.getAntecedentId()).isEqualTo(media.getAntecedentId()); + assertThat(response.getIsCurrent()).isEqualTo(media.getIsCurrent()); + assertThat(response.getCreatedAt()).isEqualTo(media.getCreatedDateTime()); + + verify(courtroomMapper).toApiModel(courtroomEntity); + verify(courtroomEntity).getCourthouse(); + verify(courthouseMapper).toApiModel(courthouseEntity); + } + + @Test + void mapAdminMediaVersionResponse_ifMediaEntityIsNull_nullShouldBeReturned() { + assertThat(getAdminMediaResponseMapper.mapAdminMediaVersionResponse(null)).isNull(); + } +} diff --git a/src/test/java/uk/gov/hmcts/darts/audio/service/impl/AdminMediaServiceImplTest.java b/src/test/java/uk/gov/hmcts/darts/audio/service/impl/AdminMediaServiceImplTest.java index e2211c1319..3e026aab40 100644 --- a/src/test/java/uk/gov/hmcts/darts/audio/service/impl/AdminMediaServiceImplTest.java +++ b/src/test/java/uk/gov/hmcts/darts/audio/service/impl/AdminMediaServiceImplTest.java @@ -5,6 +5,7 @@ import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -21,6 +22,7 @@ import uk.gov.hmcts.darts.audio.component.impl.ApplyAdminActionComponent; import uk.gov.hmcts.darts.audio.component.impl.RemoveAdminActionComponent; import uk.gov.hmcts.darts.audio.entity.MediaRequestEntity; +import uk.gov.hmcts.darts.audio.exception.AudioApiError; import uk.gov.hmcts.darts.audio.mapper.AdminMarkedForDeletionMapper; import uk.gov.hmcts.darts.audio.mapper.AdminMarkedForDeletionMapperImpl; import uk.gov.hmcts.darts.audio.mapper.CourthouseMapper; @@ -33,6 +35,7 @@ import uk.gov.hmcts.darts.audio.model.AdminActionRequest; import uk.gov.hmcts.darts.audio.model.AdminMediaCourthouseResponse; import uk.gov.hmcts.darts.audio.model.AdminMediaCourtroomResponse; +import uk.gov.hmcts.darts.audio.model.AdminVersionedMediaResponse; import uk.gov.hmcts.darts.audio.model.GetAdminMediaResponseItem; import uk.gov.hmcts.darts.audio.model.GetAdminMediasMarkedForDeletionAdminAction; import uk.gov.hmcts.darts.audio.model.GetAdminMediasMarkedForDeletionItem; @@ -102,6 +105,8 @@ class AdminMediaServiceImplTest { private RemoveAdminActionComponent removeAdminActionComponent; @Mock private ObjectHiddenReasonRepository hiddenReasonRepository; + @Mock + private GetAdminMediaResponseMapper getAdminMediaResponseMapper; private ObjectMapper objectMapper; @@ -568,6 +573,155 @@ void filterMedias_shouldReturnSingleMediaForHearingsAndStartEndTimes_whenMediaIs JSONAssert.assertEquals(expectedString, responseString, JSONCompareMode.NON_EXTENSIBLE); } + @Test + void getMediaEntityById_shouldReutrnMediaEntity_ifOneExists() { + MediaEntity mediaEntity = mock(MediaEntity.class); + when(mediaRepository.findById(1)).thenReturn(Optional.of(mediaEntity)); + + assertThat(mediaRequestService.getMediaEntityById(1)) + .isEqualTo(mediaEntity); + verify(mediaRepository).findById(1); + } + + @Test + void getMediaEntityById_shouldThrowException_ifNoMediaEntityExists() { + when(mediaRepository.findById(1)).thenReturn(Optional.empty()); + + DartsApiException exception = assertThrows(DartsApiException.class, () -> mediaRequestService.getMediaEntityById(1)); + + assertThat(exception.getError()).isEqualTo(AudioApiError.MEDIA_NOT_FOUND); + } + + + @Nested + @DisplayName("AdminVersionedMediaResponse getMediaVersionsById(Integer id)") + class GetMediaVersionsById { + @Test + void getMediaVersionsById_shouldThrowException_whenChronicleIdIsNull() { + MediaEntity mediaEntity = mock(MediaEntity.class); + mediaEntity.setChronicleId(null); + doReturn(mediaEntity).when(mediaRequestService).getMediaEntityById(123); + + DartsApiException exception = assertThrows(DartsApiException.class, () -> mediaRequestService.getMediaVersionsById(123)); + assertThat(exception.getError()).isEqualTo(CommonApiError.INTERNAL_SERVER_ERROR); + assertThat(exception.getMessage()) + .isEqualTo("Internal server error. Media 123 has a Chronicle Id that is null. As such we can not ensure accurate results are returned"); + } + + @Test + void getMediaVersionsById_shouldReturnEmptyVersionList_whenNoMediaVersionsExist() { + final String chronicleId = "someChronicleId"; + MediaEntity mediaEntity = createMediaEntity(true, chronicleId, OffsetDateTime.now()); + doReturn(mediaEntity).when(mediaRequestService).getMediaEntityById(123); + when(mediaRepository.findAllByChronicleId(chronicleId)).thenReturn(List.of(mediaEntity)); + + AdminVersionedMediaResponse response = mock(AdminVersionedMediaResponse.class); + when(getAdminMediaResponseMapper.mapAdminVersionedMediaResponse(mediaEntity, List.of())).thenReturn(response); + + assertThat(mediaRequestService.getMediaVersionsById(123)) + .isEqualTo(response); + + verify(mediaRepository).findAllByChronicleId(chronicleId); + verify(getAdminMediaResponseMapper).mapAdminVersionedMediaResponse(mediaEntity, List.of()); + verify(mediaRequestService).getMediaEntityById(123); + } + + @Test + void getMediaVersionsById_shouldReturnVersionsAndCurrentMedia_whenVersionsExist() { + final String chronicleId = "someChronicleId"; + OffsetDateTime now = OffsetDateTime.now(); + MediaEntity currentMediaEntity = createMediaEntity(true, chronicleId, now); + MediaEntity versionedMediaEntity1 = createMediaEntity(null, chronicleId, now.plusMinutes(2)); + MediaEntity versionedMediaEntity2 = createMediaEntity(false, chronicleId, now.plusMinutes(1)); + + doReturn(currentMediaEntity).when(mediaRequestService).getMediaEntityById(123); + when(mediaRepository.findAllByChronicleId(chronicleId)) + .thenReturn(List.of(currentMediaEntity, versionedMediaEntity2, versionedMediaEntity1)); + + AdminVersionedMediaResponse response = mock(AdminVersionedMediaResponse.class); + + List expectedVersioendMedia = List.of(versionedMediaEntity1, versionedMediaEntity2); + when(getAdminMediaResponseMapper.mapAdminVersionedMediaResponse(currentMediaEntity, expectedVersioendMedia)) + .thenReturn(response); + + assertThat(mediaRequestService.getMediaVersionsById(123)) + .isEqualTo(response); + + verify(mediaRepository).findAllByChronicleId(chronicleId); + verify(getAdminMediaResponseMapper).mapAdminVersionedMediaResponse(currentMediaEntity, expectedVersioendMedia); + verify(mediaRequestService).getMediaEntityById(123); + } + + + @Test + void getMediaVersionsById_shouldReturnNullCurrentVersion_ifAllMediaIsCurrentFlase() { + final String chronicleId = "someChronicleId"; + OffsetDateTime now = OffsetDateTime.now(); + MediaEntity versionedMediaEntity1 = createMediaEntity(null, chronicleId, now.plusMinutes(2)); + MediaEntity versionedMediaEntity2 = createMediaEntity(false, chronicleId, now.plusMinutes(1)); + + doReturn(versionedMediaEntity1).when(mediaRequestService).getMediaEntityById(123); + + + when(mediaRepository.findAllByChronicleId(chronicleId)) + .thenReturn(List.of(versionedMediaEntity2, versionedMediaEntity1)); + + AdminVersionedMediaResponse response = mock(AdminVersionedMediaResponse.class); + + List expectedVersioendMedia = List.of(versionedMediaEntity1, versionedMediaEntity2); + when(getAdminMediaResponseMapper.mapAdminVersionedMediaResponse(null, expectedVersioendMedia)) + .thenReturn(response); + + assertThat(mediaRequestService.getMediaVersionsById(123)) + .isEqualTo(response); + + verify(mediaRepository).findAllByChronicleId(chronicleId); + verify(getAdminMediaResponseMapper).mapAdminVersionedMediaResponse(null, expectedVersioendMedia); + verify(mediaRequestService).getMediaEntityById(123); + + } + + @Test + void getMediaVersionsById_shouldReturnLastCreatedMedia_ifMultipleIsCurrentTrueExist() { + final String chronicleId = "someChronicleId"; + OffsetDateTime now = OffsetDateTime.now(); + MediaEntity currentMediaEntity1 = createMediaEntity(true, chronicleId, now.plusMinutes(2)); + MediaEntity currentMediaEntity2 = createMediaEntity(true, chronicleId, now); + MediaEntity currentMediaEntity3 = createMediaEntity(true, chronicleId, now.plusMinutes(1)); + + MediaEntity versionedMediaEntity1 = createMediaEntity(null, chronicleId, now.plusMinutes(2)); + MediaEntity versionedMediaEntity2 = createMediaEntity(false, chronicleId, now.plusMinutes(1)); + + doReturn(currentMediaEntity1).when(mediaRequestService).getMediaEntityById(123); + when(mediaRepository.findAllByChronicleId(chronicleId)) + .thenReturn(List.of(currentMediaEntity1, currentMediaEntity2, currentMediaEntity3, versionedMediaEntity2, versionedMediaEntity1)); + AdminVersionedMediaResponse response = mock(AdminVersionedMediaResponse.class); + + + List expectedVersioendMedia = List.of(currentMediaEntity3, currentMediaEntity2, versionedMediaEntity1, versionedMediaEntity2); + when(getAdminMediaResponseMapper.mapAdminVersionedMediaResponse(currentMediaEntity1, expectedVersioendMedia)) + .thenReturn(response); + + assertThat(mediaRequestService.getMediaVersionsById(123)) + .isEqualTo(response); + + verify(mediaRepository).findAllByChronicleId(chronicleId); + verify(getAdminMediaResponseMapper).mapAdminVersionedMediaResponse(currentMediaEntity1, expectedVersioendMedia); + verify(mediaRequestService).getMediaEntityById(123); + + } + + private MediaEntity createMediaEntity(Boolean isCurrent, String chronicleId, OffsetDateTime offsetDateTime) { + return PersistableFactory.getMediaTestData().someMinimalBuilder() + .chronicleId(chronicleId) + .isCurrent(isCurrent) + .createdDateTime(offsetDateTime) + .build() + .getEntity(); + } + } + + @NotNull private static HearingEntity createHearing() { CourthouseEntity courthouse = new CourthouseEntity(); @@ -828,7 +982,7 @@ void shouldThrowException_whenNoMediaIsFound() { // When IllegalStateException exception = assertThrows(IllegalStateException.class, - () -> mediaRequestService.adminHideOrShowMediaById(1, mediaHideRequest)); + () -> mediaRequestService.adminHideOrShowMediaById(1, mediaHideRequest)); // Then assertEquals(exception.getMessage(), "Media not found, expected this to be pre-validated");