diff --git a/build.gradle b/build.gradle index a47f7d413..4f279ae80 100644 --- a/build.gradle +++ b/build.gradle @@ -187,7 +187,9 @@ def coverageExclusionList = [ ] def duplicateCodeExclusionList = [ - '**uk/gov/hmcts/reform/preapi/entities/**' + '**uk/gov/hmcts/reform/preapi/entities/**', + '**uk/gov/hmcts/reform/preapi/controllers/LegacyReportController*', + '**uk/gov/hmcts/reform/preapi/services/LegacyReportService*' ] sonarqube { diff --git a/docker-compose.yml b/docker-compose.yml index 6a16fbe57..44b2fca27 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,8 @@ services: POSTGRES_DB: api POSTGRES_PASSWORD: pre POSTGRES_USER: pre + TZ: 'UTC' + PGTZ: 'UTC' ports: - 5432:5432 diff --git a/docker/database/init-db.sh b/docker/database/init-db.sh index fe733311a..164f9fcef 100644 --- a/docker/database/init-db.sh +++ b/docker/database/init-db.sh @@ -4,4 +4,5 @@ set -e psql -v ON_ERROR_STOP=1 --username postgres <<-EOSQL CREATE EXTENSION IF NOT EXISTS pgcrypto; + SET TIME ZONE 'UTC'; EOSQL diff --git a/infrastructure/main.tf b/infrastructure/main.tf index fd87685ce..5582f3f5c 100644 --- a/infrastructure/main.tf +++ b/infrastructure/main.tf @@ -41,7 +41,7 @@ module "pre_api" { api_mgmt_rg = "ss-${var.env}-network-rg" api_mgmt_name = "sds-api-mgmt-${var.env}" display_name = "Pre Recorded Evidence API" - revision = "103" + revision = "104" product_id = module.pre_product[0].product_id path = "pre-api" service_url = local.apim_service_url diff --git a/pre-api-stg.yaml b/pre-api-stg.yaml index e3ed3b942..23e9aa216 100644 --- a/pre-api-stg.yaml +++ b/pre-api-stg.yaml @@ -375,6 +375,9 @@ definitions: CourtDTO: description: CourtDTO properties: + county: + description: CourtCounty + type: string court_type: description: CourtType enum: @@ -392,6 +395,9 @@ definitions: name: description: CourtName type: string + postcode: + description: CourtPostcode + type: string regions: description: CourtRegions items: @@ -605,6 +611,9 @@ definitions: CreateCourtDTO: description: CreateCourtDTO properties: + county: + description: CreateCourtCounty + type: string court_type: description: CreateCourtType enum: @@ -622,6 +631,10 @@ definitions: name: description: CreateCourtName type: string + postcode: + description: CreateCourtPostcode + pattern: '^[A-Z]{1,2}[0-9][0-9A-Z]? [0-9][A-Z]{2}$' + type: string regions: description: CreateCourtRegionIds items: @@ -1029,6 +1042,9 @@ definitions: properties: _links: $ref: '#/definitions/Links' + county: + description: CourtCounty + type: string court_type: description: CourtType enum: @@ -1046,6 +1062,9 @@ definitions: name: description: CourtName type: string + postcode: + description: CourtPostcode + type: string regions: description: CourtRegions items: @@ -3390,6 +3409,273 @@ paths: summary: Revert deletion of a recording tags: - recording-controller + /reports-v2/capture-sessions-concurrent: + get: + operationId: reportConcurrentCaptureSessions_1 + parameters: + - description: The User Id of the User making the request + format: uuid + in: header + name: X-User-Id + required: false + type: string + x-example: 123e4567-e89b-12d3-a456-426614174000 + produces: + - application/octet-stream + responses: + '200': + description: OK + schema: + items: + $ref: '#/definitions/ConcurrentCaptureSessionReportDTO' + type: array + tags: + - report-controller + /reports-v2/completed-capture-sessions: + get: + operationId: reportCompletedCaptureSessions_1 + parameters: + - description: The User Id of the User making the request + format: uuid + in: header + name: X-User-Id + required: false + type: string + x-example: 123e4567-e89b-12d3-a456-426614174000 + produces: + - application/octet-stream + responses: + '200': + description: OK + schema: + items: + $ref: '#/definitions/CompletedCaptureSessionReportDTO' + type: array + summary: Get a report on capture sessions with available recordings + tags: + - report-controller + /reports-v2/edits: + get: + operationId: reportEdits_1 + parameters: + - description: The User Id of the User making the request + format: uuid + in: header + name: X-User-Id + required: false + type: string + x-example: 123e4567-e89b-12d3-a456-426614174000 + produces: + - application/octet-stream + responses: + '200': + description: OK + schema: + items: + $ref: '#/definitions/EditReportDTO' + type: array + summary: Get a report on recordings edits + tags: + - report-controller + /reports-v2/playback: + get: + operationId: reportPlayback_1 + parameters: + - description: 'The source of the playback. Only accepts PORTAL, APPLICATION or null' + enum: + - APPLICATION + - PORTAL + - ADMIN + - AUTO + in: query + name: source + required: false + type: string + - description: The User Id of the User making the request + format: uuid + in: header + name: X-User-Id + required: false + type: string + x-example: 123e4567-e89b-12d3-a456-426614174000 + produces: + - application/octet-stream + responses: + '200': + description: OK + schema: + items: + $ref: '#/definitions/PlaybackReportDTO' + type: array + summary: 'Get report on playback by playback source (PORTAL, APPLICATION)' + tags: + - report-controller + /reports-v2/recording-participants: + get: + operationId: reportRecordingParticipants_1 + parameters: + - description: The User Id of the User making the request + format: uuid + in: header + name: X-User-Id + required: false + type: string + x-example: 123e4567-e89b-12d3-a456-426614174000 + produces: + - application/octet-stream + responses: + '200': + description: OK + schema: + items: + $ref: '#/definitions/RecordingParticipantsReportDTO' + type: array + summary: Get report on participants and the recordings they are part of + tags: + - report-controller + /reports-v2/recordings-per-case: + get: + operationId: reportRecordingsPerCase_1 + parameters: + - description: The User Id of the User making the request + format: uuid + in: header + name: X-User-Id + required: false + type: string + x-example: 123e4567-e89b-12d3-a456-426614174000 + produces: + - application/octet-stream + responses: + '200': + description: OK + schema: + items: + $ref: '#/definitions/RecordingsPerCaseReportDTO' + type: array + summary: Get the number of completed capture sessions for each case + tags: + - report-controller + /reports-v2/schedules: + get: + operationId: reportSchedules_1 + parameters: + - description: The User Id of the User making the request + format: uuid + in: header + name: X-User-Id + required: false + type: string + x-example: 123e4567-e89b-12d3-a456-426614174000 + produces: + - application/octet-stream + responses: + '200': + description: OK + schema: + items: + $ref: '#/definitions/ScheduleReportDTO' + type: array + summary: Get a list of completed capture sessions with booking details + tags: + - report-controller + /reports-v2/share-bookings-removed: + get: + operationId: reportShareBookingRemoved_1 + parameters: + - description: The User Id of the User making the request + format: uuid + in: header + name: X-User-Id + required: false + type: string + x-example: 123e4567-e89b-12d3-a456-426614174000 + produces: + - application/octet-stream + responses: + '200': + description: OK + schema: + items: + $ref: '#/definitions/AccessRemovedReportDTO' + type: array + summary: Get report on booking share removal + tags: + - report-controller + /reports-v2/shared-bookings: + get: + operationId: reportBookingsShared_1 + parameters: + - description: The court id to search by + format: uuid + in: query + name: courtId + type: string + x-example: 123e4567-e89b-12d3-a456-426614174000 + - description: The booking id to search by + format: uuid + in: query + name: bookingId + type: string + x-example: 123e4567-e89b-12d3-a456-426614174000 + - description: The id of the user the booking is shared with to search by + format: uuid + in: query + name: sharedWithId + type: string + x-example: 123e4567-e89b-12d3-a456-426614174000 + - description: The email of the user the booking is shared with to search by + in: query + name: sharedWithEmail + type: string + x-example: example@example.com + - description: 'The shares must be active (not deleted) then true, otherwise false' + in: query + name: onlyActive + type: boolean + x-example: true + - description: The User Id of the User making the request + format: uuid + in: header + name: X-User-Id + required: false + type: string + x-example: 123e4567-e89b-12d3-a456-426614174000 + produces: + - application/octet-stream + responses: + '200': + description: OK + schema: + items: + $ref: '#/definitions/SharedReportDTO' + type: array + summary: Get a report on the bookings that have been shared + tags: + - report-controller + /reports-v2/user-primary-courts: + get: + operationId: reportUserPrimaryCourts_1 + parameters: + - description: The User Id of the User making the request + format: uuid + in: header + name: X-User-Id + required: false + type: string + x-example: 123e4567-e89b-12d3-a456-426614174000 + produces: + - application/octet-stream + responses: + '200': + description: OK + schema: + items: + $ref: '#/definitions/UserPrimaryCourtReportDTO' + type: array + summary: Get report on app users and their primary courts + tags: + - report-controller /reports/capture-sessions-concurrent: get: operationId: reportConcurrentCaptureSessions @@ -3411,7 +3697,7 @@ paths: $ref: '#/definitions/ConcurrentCaptureSessionReportDTO' type: array tags: - - report-controller + - legacy-report-controller /reports/completed-capture-sessions: get: operationId: reportCompletedCaptureSessions @@ -3434,7 +3720,7 @@ paths: type: array summary: Get a report on capture sessions with available recordings tags: - - report-controller + - legacy-report-controller /reports/edits: get: operationId: reportEdits @@ -3457,7 +3743,7 @@ paths: type: array summary: Get a report on recordings edits tags: - - report-controller + - legacy-report-controller /reports/playback: get: operationId: reportPlayback @@ -3490,7 +3776,7 @@ paths: type: array summary: 'Get report on playback by playback source (PORTAL, APPLICATION)' tags: - - report-controller + - legacy-report-controller /reports/recording-participants: get: operationId: reportRecordingParticipants @@ -3513,7 +3799,7 @@ paths: type: array summary: Get report on participants and the recordings they are part of tags: - - report-controller + - legacy-report-controller /reports/recordings-per-case: get: operationId: reportRecordingsPerCase @@ -3536,7 +3822,7 @@ paths: type: array summary: Get the number of completed capture sessions for each case tags: - - report-controller + - legacy-report-controller /reports/schedules: get: operationId: reportSchedules @@ -3559,7 +3845,7 @@ paths: type: array summary: Get a list of completed capture sessions with booking details tags: - - report-controller + - legacy-report-controller /reports/share-bookings-removed: get: operationId: reportShareBookingRemoved @@ -3582,7 +3868,7 @@ paths: type: array summary: Get report on booking share removal tags: - - report-controller + - legacy-report-controller /reports/shared-bookings: get: operationId: reportBookingsShared @@ -3628,7 +3914,7 @@ paths: type: array summary: Get a report on the bookings that have been shared tags: - - report-controller + - legacy-report-controller /reports/user-primary-courts: get: operationId: reportUserPrimaryCourts @@ -3649,9 +3935,12 @@ paths: items: $ref: '#/definitions/UserPrimaryCourtReportDTO' type: array - summary: Get report on app users and their primary courts + summary: >- + Get report on app users: their first and last name, their role, their + active status, their primary court and their last access time (if + available) tags: - - report-controller + - legacy-report-controller /roles: get: operationId: getRoles diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/LegacyReportControllerFT.java b/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/LegacyReportControllerFT.java new file mode 100644 index 000000000..dbe001ecb --- /dev/null +++ b/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/LegacyReportControllerFT.java @@ -0,0 +1,38 @@ +package uk.gov.hmcts.reform.preapi.controllers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import uk.gov.hmcts.reform.preapi.controllers.params.TestingSupportRoles; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.ConcurrentCaptureSessionReportDTO; +import uk.gov.hmcts.reform.preapi.util.FunctionalTestBase; + +import static org.assertj.core.api.Assertions.assertThat; + +public class LegacyReportControllerFT extends FunctionalTestBase { + @DisplayName("Scenario: Should format Duration and date correctly") + @Test + void shouldFormatDurationAndDateCorrectly() throws JsonProcessingException { + var captureSessionId = doPostRequest("/testing-support/should-delete-recordings-for-booking", + TestingSupportRoles.SUPER_USER) + .body().jsonPath().getUUID("captureSessionId"); + + var response = doGetRequest(LEGACY_REPORTS_ENDPOINT + "/capture-sessions-concurrent", + TestingSupportRoles.SUPER_USER); + var list = response.getBody().jsonPath().getList("", ConcurrentCaptureSessionReportDTO.class); + + int index = 0; + for (int i = 0; i < list.size(); i++) { + if (list.get(i).getId().equals(captureSessionId)) { + index = i; + break; + } + } + + var json = OBJECT_MAPPER.readTree(response.getBody().asString()); + + assertThat(json.get(index).get("duration").asText()).isEqualTo("PT3M"); + assertThat(json.get(index).get("start_time") + .asText()).matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.\\d{3}Z"); + } +} diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/ReportControllerFT.java b/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/ReportControllerFT.java index a27d55549..353c94509 100644 --- a/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/ReportControllerFT.java +++ b/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/ReportControllerFT.java @@ -4,34 +4,39 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import uk.gov.hmcts.reform.preapi.controllers.params.TestingSupportRoles; +import uk.gov.hmcts.reform.preapi.dto.BookingDTO; import uk.gov.hmcts.reform.preapi.dto.reports.ConcurrentCaptureSessionReportDTO; import uk.gov.hmcts.reform.preapi.util.FunctionalTestBase; +import java.util.stream.IntStream; + import static org.assertj.core.api.Assertions.assertThat; public class ReportControllerFT extends FunctionalTestBase { + + private static final String DATE_FORMAT_REGEX = "^\\d{2}/\\d{2}/\\d{4}$"; + private static final String TIME_FORMAT_REGEX = "^\\d{2}:\\d{2}:\\d{2}$"; + @DisplayName("Scenario: Should format Duration and date correctly") @Test void shouldFormatDurationAndDateCorrectly() throws JsonProcessingException { - var captureSessionId = doPostRequest("/testing-support/should-delete-recordings-for-booking", + var bookingId = doPostRequest("/testing-support/should-delete-recordings-for-booking", TestingSupportRoles.SUPER_USER) - .body().jsonPath().getUUID("captureSessionId"); + .body().jsonPath().getUUID("bookingId"); + var booking = doGetRequest("/bookings/" + bookingId, TestingSupportRoles.SUPER_USER) + .body().jsonPath().getObject("", BookingDTO.class); var response = doGetRequest(REPORTS_ENDPOINT + "/capture-sessions-concurrent", TestingSupportRoles.SUPER_USER); var list = response.getBody().jsonPath().getList("", ConcurrentCaptureSessionReportDTO.class); + var reportItemIndex = IntStream.range(0, list.size()) + .filter(i -> list.get(i).getCaseReference().equals(booking.getCaseDTO().getReference())) + .findFirst() + .orElseThrow(); - int index = 0; - for (int i = 0; i < list.size(); i++) { - if (list.get(i).getId().equals(captureSessionId)) { - index = i; - break; - } - } - - var json = OBJECT_MAPPER.readTree(response.getBody().asString()); - - assertThat(json.get(index).get("duration").asText()).isEqualTo("PT3M"); - assertThat(json.get(index).get("start_time") - .asText()).matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.\\d{3}Z"); + var jsonItem = OBJECT_MAPPER.readTree(response.getBody().asString()).get(reportItemIndex); + assertThat(jsonItem.get("date").asText()).matches(DATE_FORMAT_REGEX); + assertThat(jsonItem.get("start_time").asText()).matches(TIME_FORMAT_REGEX); + assertThat(jsonItem.get("end_time").asText()).matches(TIME_FORMAT_REGEX); + assertThat(jsonItem.get("duration").asText()).matches(TIME_FORMAT_REGEX); } } diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/preapi/util/FunctionalTestBase.java b/src/functionalTest/java/uk/gov/hmcts/reform/preapi/util/FunctionalTestBase.java index eb58cff5d..6a5514cfa 100644 --- a/src/functionalTest/java/uk/gov/hmcts/reform/preapi/util/FunctionalTestBase.java +++ b/src/functionalTest/java/uk/gov/hmcts/reform/preapi/util/FunctionalTestBase.java @@ -59,7 +59,8 @@ public class FunctionalTestBase { protected static final String USERS_ENDPOINT = "/users"; protected static final String INVITES_ENDPOINT = "/invites"; protected static final String LOCATION_HEADER = "Location"; - protected static final String REPORTS_ENDPOINT = "/reports"; + protected static final String LEGACY_REPORTS_ENDPOINT = "/reports"; + protected static final String REPORTS_ENDPOINT = "/reports-v2"; protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/preapi/services/LegacyReportServiceIT.java b/src/integrationTest/java/uk/gov/hmcts/reform/preapi/services/LegacyReportServiceIT.java new file mode 100644 index 000000000..18c802ff9 --- /dev/null +++ b/src/integrationTest/java/uk/gov/hmcts/reform/preapi/services/LegacyReportServiceIT.java @@ -0,0 +1,372 @@ +package uk.gov.hmcts.reform.preapi.services; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.transaction.Transactional; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.ConcurrentCaptureSessionReportDTO; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.RecordingsPerCaseReportDTO; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.SharedReportDTO; +import uk.gov.hmcts.reform.preapi.entities.Audit; +import uk.gov.hmcts.reform.preapi.entities.Booking; +import uk.gov.hmcts.reform.preapi.entities.CaptureSession; +import uk.gov.hmcts.reform.preapi.entities.Case; +import uk.gov.hmcts.reform.preapi.entities.Court; +import uk.gov.hmcts.reform.preapi.entities.Recording; +import uk.gov.hmcts.reform.preapi.entities.Region; +import uk.gov.hmcts.reform.preapi.entities.ShareBooking; +import uk.gov.hmcts.reform.preapi.entities.User; +import uk.gov.hmcts.reform.preapi.enums.AuditLogSource; +import uk.gov.hmcts.reform.preapi.enums.CourtType; +import uk.gov.hmcts.reform.preapi.enums.RecordingOrigin; +import uk.gov.hmcts.reform.preapi.enums.RecordingStatus; +import uk.gov.hmcts.reform.preapi.util.HelperFactory; +import uk.gov.hmcts.reform.preapi.utils.IntegrationTestBase; + +import java.sql.Timestamp; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class LegacyReportServiceIT extends IntegrationTestBase { + @Autowired + private LegacyReportService reportService; + + @Transactional + @Test + public void reportSharedSuccess() { + var court = HelperFactory.createCourt(CourtType.CROWN, "Example court", "12458"); + entityManager.persist(court); + + var caseEntity = HelperFactory.createCase(court, "CASE12345", true, null); + entityManager.persist(caseEntity); + + var booking = HelperFactory.createBooking(caseEntity, Timestamp.from(Instant.now()), null); + entityManager.persist(booking); + + var user1 = HelperFactory.createUser("Example", "One", "example1@example.com", null, null, null); + entityManager.persist(user1); + + var user2 = HelperFactory.createUser("Example", "Two", "example2@example.com", null, null, null); + entityManager.persist(user2); + + var share = HelperFactory.createShareBooking(user1, user2, booking, null); + entityManager.persist(share); + + var reportFilterNone = reportService.reportShared(null, null, null, null); + assertSharedReportSuccess(court, caseEntity, booking, user1, user2, share, reportFilterNone); + + var reportFilterCourt = reportService.reportShared(court.getId(), null, null, null); + assertSharedReportSuccess(court, caseEntity, booking, user1, user2, share, reportFilterCourt); + + var reportFilterBooking = reportService.reportShared(null, booking.getId(), null, null); + assertSharedReportSuccess(court, caseEntity, booking, user1, user2, share, reportFilterBooking); + + var reportFilterUserId = reportService.reportShared(null, null, user1.getId(), null); + assertSharedReportSuccess(court, caseEntity, booking, user1, user2, share, reportFilterUserId); + + var reportFilterUserEmail = reportService.reportShared(null, null, null, user1.getEmail()); + assertSharedReportSuccess(court, caseEntity, booking, user1, user2, share, reportFilterUserEmail); + + var reportFilterNotFound1 = reportService.reportShared(UUID.randomUUID(), null, null, null); + assertThat(reportFilterNotFound1.isEmpty()).isTrue(); + + var reportFilterNotFound2 = reportService.reportShared(null, UUID.randomUUID(), null, null); + assertThat(reportFilterNotFound2.isEmpty()).isTrue(); + + var reportFilterNotFound3 = reportService.reportShared(null, null, UUID.randomUUID(), null); + assertThat(reportFilterNotFound3.isEmpty()).isTrue(); + + var reportFilterNotFound4 = reportService.reportShared(null, null, null, "test@test.com"); + assertThat(reportFilterNotFound4.isEmpty()).isTrue(); + } + + @Transactional + @Test + public void reportSharedAllRecordingIdNull() { + var court = HelperFactory.createCourt(CourtType.CROWN, "Example court", "12458"); + entityManager.persist(court); + + var caseEntity = HelperFactory.createCase(court, "CASE12345", true, null); + entityManager.persist(caseEntity); + + var booking = HelperFactory.createBooking(caseEntity, Timestamp.from(Instant.now()), null); + entityManager.persist(booking); + + var captureSession = HelperFactory.createCaptureSession( + booking, + RecordingOrigin.PRE, + null, + null, + null, + null, + null, + null, + RecordingStatus.RECORDING_AVAILABLE, + null + ); + entityManager.persist(captureSession); + + var recording = HelperFactory.createRecording(captureSession, null, 1, "example.file", null); + entityManager.persist(recording); + + var user = HelperFactory.createUser("Example", "One", "example1@example.com", null, null, null); + entityManager.persist(user); + + var audit1 = new Audit(); + audit1.setId(UUID.randomUUID()); + audit1.setActivity("Recording Playback started"); + audit1.setCreatedBy(user.getId()); + audit1.setSource(AuditLogSource.APPLICATION); + var mapper = new ObjectMapper(); + audit1.setAuditDetails(mapper.valueToTree(new HashMap() {{ + put("description", "Playback on recording has started"); + put("recordingId", null); + }} + )); + entityManager.persist(audit1); + + var audit2 = new Audit(); + audit2.setId(UUID.randomUUID()); + audit2.setActivity("Play"); + audit2.setCreatedBy(user.getId()); + audit2.setSource(AuditLogSource.APPLICATION); + audit2.setAuditDetails(mapper.valueToTree(new HashMap() {{ + put("details", "Confirmed viewing"); + put("recordingId", null); + }} + )); + entityManager.persist(audit2); + + var response = reportService.reportPlayback(null); + assertThat(response).isNotNull(); + assertThat(response.size()).isEqualTo(2); + assertThat(response.getFirst().getRecordingId()).isNull(); + } + + @Transactional + @Test + void reportRecordingsPerCaseSuccess() { + var region = HelperFactory.createRegion("Example Region", Set.of()); + entityManager.persist(region); + + var court = HelperFactory.createCourt(CourtType.CROWN, "Example court", "12458"); + court.setRegions(Set.of(region)); + entityManager.persist(court); + + var aCase1 = HelperFactory.createCase(court, "CASE12345", true, null); + entityManager.persist(aCase1); + var aCase2 = HelperFactory.createCase(court, "CASE67890", true, null); + entityManager.persist(aCase2); + + var booking1 = HelperFactory.createBooking(aCase1, Timestamp.from(Instant.now()), null); + entityManager.persist(booking1); + var booking2 = HelperFactory.createBooking(aCase1, Timestamp.from(Instant.now()), null); + entityManager.persist(booking2); + var booking3 = HelperFactory.createBooking(aCase2, Timestamp.from(Instant.now()), null); + entityManager.persist(booking3); + + var captureSession1 = HelperFactory.createCaptureSession( + booking1, + RecordingOrigin.PRE, + null, + null, + Timestamp.from(Instant.now()), + null, + Timestamp.from(Instant.now()), + null, + RecordingStatus.RECORDING_AVAILABLE, + null + ); + entityManager.persist(captureSession1); + var captureSession2 = HelperFactory.createCaptureSession( + booking2, + RecordingOrigin.PRE, + null, + null, + Timestamp.from(Instant.now()), + null, + Timestamp.from(Instant.now()), + null, + RecordingStatus.RECORDING_AVAILABLE, + null + ); + entityManager.persist(captureSession2); + var captureSession3 = HelperFactory.createCaptureSession( + booking3, + RecordingOrigin.PRE, + null, + null, + Timestamp.from(Instant.now()), + null, + Timestamp.from(Instant.now()), + null, + RecordingStatus.RECORDING_AVAILABLE, + null + ); + entityManager.persist(captureSession3); + + var recording1 = HelperFactory.createRecording(captureSession1, null, 1, "example.file", null); + entityManager.persist(recording1); + var recording2 = HelperFactory.createRecording(captureSession2, null, 1, "example.file", null); + entityManager.persist(recording2); + var recording3 = HelperFactory.createRecording(captureSession3, null, 1, "example.file", null); + entityManager.persist(recording3); + var recording4 = HelperFactory.createRecording(captureSession3, recording3, 2, "example.file", null); + entityManager.persist(recording4); + + var report = reportService.reportRecordingsPerCase(); + + assertThat(report).isNotNull(); + assertThat(report.size()).isEqualTo(2); + + var recordingsForCase1 = report + .stream() + .filter(r -> r.getCaseReference().equals(aCase1.getReference())) + .findFirst(); + assertThat(recordingsForCase1).isPresent(); + assertRecordingPerCaseSuccess(recordingsForCase1.get(), aCase1, court, region, 2); + + var recordingsForCase2 = report + .stream() + .filter(r -> r.getCaseReference().equals(aCase2.getReference())) + .findFirst(); + assertThat(recordingsForCase2).isPresent(); + assertRecordingPerCaseSuccess(recordingsForCase2.get(), aCase2, court, region, 1); + } + + @Transactional + @Test + void reportConcurrentCaptureSessionsSuccess() { + var region = HelperFactory.createRegion("Example Region", Set.of()); + entityManager.persist(region); + + var court = HelperFactory.createCourt(CourtType.CROWN, "Example court", "12458"); + court.setRegions(Set.of(region)); + entityManager.persist(court); + + var aCase = HelperFactory.createCase(court, "CASE12345", true, null); + entityManager.persist(aCase); + + var booking1 = HelperFactory.createBooking(aCase, Timestamp.from(Instant.now()), null); + entityManager.persist(booking1); + + var booking2 = HelperFactory.createBooking(aCase, Timestamp.from(Instant.now()), null); + entityManager.persist(booking2); + + var captureSession1 = HelperFactory.createCaptureSession( + booking1, + RecordingOrigin.PRE, + null, + null, + Timestamp.from(Instant.now()), + null, + Timestamp.from(Instant.now()), + null, + RecordingStatus.RECORDING_AVAILABLE, + null + ); + entityManager.persist(captureSession1); + var captureSession2 = HelperFactory.createCaptureSession( + booking2, + RecordingOrigin.PRE, + null, + null, + Timestamp.from(Instant.now()), + null, + null, + null, + RecordingStatus.RECORDING, + null + ); + entityManager.persist(captureSession2); + var captureSession3 = HelperFactory.createCaptureSession( + booking1, + RecordingOrigin.PRE, + null, + null, + Timestamp.from(Instant.now()), + null, + Timestamp.from(Instant.now()), + null, + RecordingStatus.RECORDING_AVAILABLE, + Timestamp.from(Instant.now()) + ); + entityManager.persist(captureSession3); + + var recording1 = HelperFactory.createRecording(captureSession1, null, 1, "example.file", null); + entityManager.persist(recording1); + var recording3 = HelperFactory.createRecording( + captureSession3, + null, + 1, + "example.file", + Timestamp.from(Instant.now()) + ); + entityManager.persist(recording3); + + var report = reportService.reportCaptureSessions(); + assertThat(report).isNotNull(); + assertThat(report.size()).isEqualTo(2); + + var report1 = report.stream().filter(r -> r.getId().equals(captureSession1.getId())).findFirst(); + assertThat(report1).isPresent(); + assertConcurrentCaptureSessionsSuccess(report1.get(), court, region, aCase, captureSession1, recording1); + + var report2 = report.stream().filter(r -> r.getId().equals(captureSession2.getId())).findFirst(); + assertThat(report2).isPresent(); + assertConcurrentCaptureSessionsSuccess(report2.get(), court, region, aCase, captureSession2, null); + + assertThat(report.stream().anyMatch(r -> r.getId().equals(captureSession3.getId()))).isFalse(); + } + + private void assertConcurrentCaptureSessionsSuccess(ConcurrentCaptureSessionReportDTO report, + Court court, + Region region, + Case aCase, + CaptureSession captureSession, + Recording recording) { + assertThat(report).isNotNull(); + assertThat(report.getId()).isEqualTo(captureSession.getId()); + assertThat(report.getStartTime()).isEqualTo(captureSession.getStartedAt()); + assertThat(report.getEndTime()).isEqualTo(captureSession.getFinishedAt()); + assertThat(report.getDuration()).isEqualTo((recording == null ? null : recording.getDuration())); + assertThat(report.getCaseReference()).isEqualTo(aCase.getReference()); + assertThat(report.getCourt()).isEqualTo(court.getName()); + assertThat(report.getRegion().size()).isEqualTo(1); + assertThat(report.getRegion().stream().findFirst().get().getName()) + .isEqualTo(region.getName()); + + } + + private void assertRecordingPerCaseSuccess(RecordingsPerCaseReportDTO report, + Case aCase, + Court court, + Region region, + int expectedCount) { + assertThat(report.getCaseReference()).isEqualTo(aCase.getReference()); + assertThat(report.getCourt()).isEqualTo(court.getName()); + assertThat(report.getRegions().size()).isEqualTo(1); + assertThat(report.getRegions().stream().findFirst().get().getName()) + .isEqualTo(region.getName()); + assertThat(report.getCount()).isEqualTo(expectedCount); + } + + private void assertSharedReportSuccess(Court court, Case caseEntity, Booking booking, User user1, User user2, + ShareBooking share, List report) { + assertThat(report.size()).isEqualTo(1); + + assertThat(report.getFirst().getSharedAt()).isEqualTo(share.getCreatedAt()); + assertThat(report.getFirst().getAllocatedTo()).isEqualTo(user1.getEmail()); + assertThat(report.getFirst().getAllocatedToFullName()).isEqualTo(user1.getFullName()); + assertThat(report.getFirst().getAllocatedBy()).isEqualTo(user2.getEmail()); + assertThat(report.getFirst().getAllocatedByFullName()).isEqualTo(user2.getFullName()); + assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(report.getFirst().getCourt()).isEqualTo(court.getName()); + assertThat(report.getFirst().getBookingId()).isEqualTo(booking.getId()); + } +} diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/preapi/services/ReportServiceIT.java b/src/integrationTest/java/uk/gov/hmcts/reform/preapi/services/ReportServiceIT.java index 60e7cfb89..77a757fd3 100644 --- a/src/integrationTest/java/uk/gov/hmcts/reform/preapi/services/ReportServiceIT.java +++ b/src/integrationTest/java/uk/gov/hmcts/reform/preapi/services/ReportServiceIT.java @@ -1,14 +1,11 @@ package uk.gov.hmcts.reform.preapi.services; -import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.transaction.Transactional; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import uk.gov.hmcts.reform.preapi.dto.reports.ConcurrentCaptureSessionReportDTO; import uk.gov.hmcts.reform.preapi.dto.reports.RecordingsPerCaseReportDTO; import uk.gov.hmcts.reform.preapi.dto.reports.SharedReportDTO; -import uk.gov.hmcts.reform.preapi.entities.Audit; -import uk.gov.hmcts.reform.preapi.entities.Booking; import uk.gov.hmcts.reform.preapi.entities.CaptureSession; import uk.gov.hmcts.reform.preapi.entities.Case; import uk.gov.hmcts.reform.preapi.entities.Court; @@ -16,17 +13,17 @@ import uk.gov.hmcts.reform.preapi.entities.Region; import uk.gov.hmcts.reform.preapi.entities.ShareBooking; import uk.gov.hmcts.reform.preapi.entities.User; -import uk.gov.hmcts.reform.preapi.enums.AuditLogSource; import uk.gov.hmcts.reform.preapi.enums.CourtType; import uk.gov.hmcts.reform.preapi.enums.ParticipantType; import uk.gov.hmcts.reform.preapi.enums.RecordingOrigin; import uk.gov.hmcts.reform.preapi.enums.RecordingStatus; import uk.gov.hmcts.reform.preapi.util.HelperFactory; +import uk.gov.hmcts.reform.preapi.utils.DateTimeUtils; import uk.gov.hmcts.reform.preapi.utils.IntegrationTestBase; import java.sql.Timestamp; +import java.time.Duration; import java.time.Instant; -import java.util.HashMap; import java.util.List; import java.util.Set; import java.util.UUID; @@ -37,10 +34,14 @@ public class ReportServiceIT extends IntegrationTestBase { @Autowired private ReportService reportService; - @Transactional @Test + @Transactional public void reportSharedSuccess() { + var region = HelperFactory.createRegion("Example Region", Set.of()); + entityManager.persist(region); + var court = HelperFactory.createCourt(CourtType.CROWN, "Example court", "12458"); + court.setRegions(Set.of(region)); entityManager.persist(court); var caseEntity = HelperFactory.createCase(court, "CASE12345", true, null); @@ -58,95 +59,41 @@ public void reportSharedSuccess() { var share = HelperFactory.createShareBooking(user1, user2, booking, null); entityManager.persist(share); - var reportFilterNone = reportService.reportShared(null, null, null, null); - assertSharedReportSuccess(court, caseEntity, booking, user1, user2, share, reportFilterNone); + var reportFilterNone = reportService.reportShared(null, null, null, null, false); + assertSharedReportSuccess(court, caseEntity, user1, user2, share, reportFilterNone); - var reportFilterCourt = reportService.reportShared(court.getId(), null, null, null); - assertSharedReportSuccess(court, caseEntity, booking, user1, user2, share, reportFilterCourt); + var reportFilterCourt = reportService.reportShared(court.getId(), null, null, null, false); + assertSharedReportSuccess(court, caseEntity, user1, user2, share, reportFilterCourt); - var reportFilterBooking = reportService.reportShared(null, booking.getId(), null, null); - assertSharedReportSuccess(court, caseEntity, booking, user1, user2, share, reportFilterBooking); + var reportFilterBooking = reportService.reportShared(null, booking.getId(), null, null, false); + assertSharedReportSuccess(court, caseEntity, user1, user2, share, reportFilterBooking); - var reportFilterUserId = reportService.reportShared(null, null, user1.getId(), null); - assertSharedReportSuccess(court, caseEntity, booking, user1, user2, share, reportFilterUserId); + var reportFilterUserId = reportService.reportShared(null, null, user1.getId(), null, false); + assertSharedReportSuccess(court, caseEntity, user1, user2, share, reportFilterUserId); - var reportFilterUserEmail = reportService.reportShared(null, null, null, user1.getEmail()); - assertSharedReportSuccess(court, caseEntity, booking, user1, user2, share, reportFilterUserEmail); + var reportFilterUserEmail = reportService.reportShared(null, null, null, user1.getEmail(), false); + assertSharedReportSuccess(court, caseEntity, user1, user2, share, reportFilterUserEmail); - var reportFilterNotFound1 = reportService.reportShared(UUID.randomUUID(), null, null, null); + var reportFilterNotFound1 = reportService.reportShared(UUID.randomUUID(), null, null, null, false); assertThat(reportFilterNotFound1.isEmpty()).isTrue(); - var reportFilterNotFound2 = reportService.reportShared(null, UUID.randomUUID(), null, null); + var reportFilterNotFound2 = reportService.reportShared(null, UUID.randomUUID(), null, null, false); assertThat(reportFilterNotFound2.isEmpty()).isTrue(); - var reportFilterNotFound3 = reportService.reportShared(null, null, UUID.randomUUID(), null); + var reportFilterNotFound3 = reportService.reportShared(null, null, UUID.randomUUID(), null, false); assertThat(reportFilterNotFound3.isEmpty()).isTrue(); - var reportFilterNotFound4 = reportService.reportShared(null, null, null, "test@test.com"); + var reportFilterNotFound4 = reportService.reportShared(null, null, null, "test@test.com", false); assertThat(reportFilterNotFound4.isEmpty()).isTrue(); - } - @Transactional - @Test - public void reportSharedAllRecordingIdNull() { - var court = HelperFactory.createCourt(CourtType.CROWN, "Example court", "12458"); - entityManager.persist(court); - - var caseEntity = HelperFactory.createCase(court, "CASE12345", true, null); - entityManager.persist(caseEntity); - - var booking = HelperFactory.createBooking(caseEntity, Timestamp.from(Instant.now()), null); - entityManager.persist(booking); - - var captureSession = HelperFactory.createCaptureSession( - booking, - RecordingOrigin.PRE, - null, - null, - null, - null, - null, - null, - RecordingStatus.RECORDING_AVAILABLE, - null - ); - entityManager.persist(captureSession); + var reportFilterOnlyActiveFound = reportService.reportShared(null, null, null, null, true); + assertSharedReportSuccess(court, caseEntity, user1, user2, share, reportFilterOnlyActiveFound); - var recording = HelperFactory.createRecording(captureSession, null, 1, "example.file", null); - entityManager.persist(recording); + share.setDeletedAt(Timestamp.from(Instant.now())); + entityManager.persist(share); - var user = HelperFactory.createUser("Example", "One", "example1@example.com", null, null, null); - entityManager.persist(user); - - var audit1 = new Audit(); - audit1.setId(UUID.randomUUID()); - audit1.setActivity("Recording Playback started"); - audit1.setCreatedBy(user.getId()); - audit1.setSource(AuditLogSource.APPLICATION); - var mapper = new ObjectMapper(); - audit1.setAuditDetails(mapper.valueToTree(new HashMap() {{ - put("description", "Playback on recording has started"); - put("recordingId", null); - }} - )); - entityManager.persist(audit1); - - var audit2 = new Audit(); - audit2.setId(UUID.randomUUID()); - audit2.setActivity("Play"); - audit2.setCreatedBy(user.getId()); - audit2.setSource(AuditLogSource.APPLICATION); - audit2.setAuditDetails(mapper.valueToTree(new HashMap() {{ - put("details", "Confirmed viewing"); - put("recordingId", null); - }} - )); - entityManager.persist(audit2); - - var response = reportService.reportPlayback(null); - assertThat(response).isNotNull(); - assertThat(response.size()).isEqualTo(2); - assertThat(response.getFirst().getRecordingId()).isNull(); + var reportFilterOnlyActiveNotFound = reportService.reportShared(null, null, null, null, true); + assertThat(reportFilterOnlyActiveNotFound.isEmpty()).isTrue(); } @Transactional @@ -354,7 +301,10 @@ void reportConcurrentCaptureSessionsSuccess() { entityManager.persist(captureSession3); var recording1 = HelperFactory.createRecording(captureSession1, null, 1, "example.file", null); + recording1.setDuration(Duration.ofMinutes(3)); + captureSession1.setRecordings(Set.of(recording1)); entityManager.persist(recording1); + entityManager.persist(captureSession1); var recording3 = HelperFactory.createRecording( captureSession3, null, @@ -362,21 +312,24 @@ void reportConcurrentCaptureSessionsSuccess() { "example.file", Timestamp.from(Instant.now()) ); + recording3.setDuration(Duration.ofMinutes(30)); entityManager.persist(recording3); var report = reportService.reportCaptureSessions(); assertThat(report).isNotNull(); assertThat(report.size()).isEqualTo(2); - var report1 = report.stream().filter(r -> r.getId().equals(captureSession1.getId())).findFirst(); + var report1 = report.stream() + .filter(r -> r.getDuration() != null).findFirst(); assertThat(report1).isPresent(); assertConcurrentCaptureSessionsSuccess(report1.get(), court, region, aCase, captureSession1, recording1); - var report2 = report.stream().filter(r -> r.getId().equals(captureSession2.getId())).findFirst(); + var report2 = report.stream().filter(r -> r.getDuration() == null).findFirst(); assertThat(report2).isPresent(); assertConcurrentCaptureSessionsSuccess(report2.get(), court, region, aCase, captureSession2, null); - assertThat(report.stream().anyMatch(r -> r.getId().equals(captureSession3.getId()))).isFalse(); + assertThat(report.stream().anyMatch(r -> r.getDuration() != null && r.getDuration().toMinutes() == 30)) + .isFalse(); } private void assertConcurrentCaptureSessionsSuccess(ConcurrentCaptureSessionReportDTO report, @@ -386,16 +339,16 @@ private void assertConcurrentCaptureSessionsSuccess(ConcurrentCaptureSessionRepo CaptureSession captureSession, Recording recording) { assertThat(report).isNotNull(); - assertThat(report.getId()).isEqualTo(captureSession.getId()); - assertThat(report.getStartTime()).isEqualTo(captureSession.getStartedAt()); - assertThat(report.getEndTime()).isEqualTo(captureSession.getFinishedAt()); + assertThat(report.getDate()).isEqualTo(DateTimeUtils.formatDate(captureSession.getStartedAt())); + assertThat(report.getStartTime()).isEqualTo(DateTimeUtils.formatTime(captureSession.getStartedAt())); + assertThat(report.getEndTime()) + .isEqualTo(captureSession.getFinishedAt() != null + ? DateTimeUtils.formatTime(captureSession.getFinishedAt()) + : null); assertThat(report.getDuration()).isEqualTo((recording == null ? null : recording.getDuration())); assertThat(report.getCaseReference()).isEqualTo(aCase.getReference()); assertThat(report.getCourt()).isEqualTo(court.getName()); - assertThat(report.getRegion().size()).isEqualTo(1); - assertThat(report.getRegion().stream().findFirst().get().getName()) - .isEqualTo(region.getName()); - + assertThat(report.getRegion()).isEqualTo(region.getName()); } private void assertRecordingPerCaseSuccess(RecordingsPerCaseReportDTO report, @@ -405,23 +358,26 @@ private void assertRecordingPerCaseSuccess(RecordingsPerCaseReportDTO report, int expectedCount) { assertThat(report.getCaseReference()).isEqualTo(aCase.getReference()); assertThat(report.getCourt()).isEqualTo(court.getName()); - assertThat(report.getRegions().size()).isEqualTo(1); - assertThat(report.getRegions().stream().findFirst().get().getName()) - .isEqualTo(region.getName()); - assertThat(report.getCount()).isEqualTo(expectedCount); + assertThat(report.getRegion()).isEqualTo(region.getName()); + assertThat(report.getNumberOfRecordings()).isEqualTo(expectedCount); } - private void assertSharedReportSuccess(Court court, Case caseEntity, Booking booking, User user1, User user2, + private void assertSharedReportSuccess(Court court, Case caseEntity, User user1, User user2, ShareBooking share, List report) { assertThat(report.size()).isEqualTo(1); - assertThat(report.getFirst().getSharedAt()).isEqualTo(share.getCreatedAt()); - assertThat(report.getFirst().getAllocatedTo()).isEqualTo(user1.getEmail()); - assertThat(report.getFirst().getAllocatedToFullName()).isEqualTo(user1.getFullName()); - assertThat(report.getFirst().getAllocatedBy()).isEqualTo(user2.getEmail()); - assertThat(report.getFirst().getAllocatedByFullName()).isEqualTo(user2.getFullName()); + assertThat(report.getFirst().getShareDate()).isEqualTo(DateTimeUtils.formatDate(share.getCreatedAt())); + assertThat(report.getFirst().getShareTime()).isEqualTo(DateTimeUtils.formatTime(share.getCreatedAt())); + assertThat(report.getFirst().getTimezone()) + .isEqualTo(DateTimeUtils.getTimezoneAbbreviation(share.getCreatedAt())); + assertThat(report.getFirst().getSharedWith()).isEqualTo(user1.getEmail()); + assertThat(report.getFirst().getSharedWithFullName()).isEqualTo(user1.getFullName()); + assertThat(report.getFirst().getGrantedBy()).isEqualTo(user2.getEmail()); + assertThat(report.getFirst().getGrantedByFullName()).isEqualTo(user2.getFullName()); assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); assertThat(report.getFirst().getCourt()).isEqualTo(court.getName()); - assertThat(report.getFirst().getBookingId()).isEqualTo(booking.getId()); + assertThat(report.getFirst().getCounty()).isEqualTo(court.getCounty()); + assertThat(report.getFirst().getPostcode()).isEqualTo(court.getPostcode()); + assertThat(report.getFirst().getRegion()).isEqualTo(court.getRegions().stream().findFirst().get().getName()); } } diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/controllers/CourtController.java b/src/main/java/uk/gov/hmcts/reform/preapi/controllers/CourtController.java index 2fc7dcafe..817caa298 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/controllers/CourtController.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/controllers/CourtController.java @@ -1,6 +1,5 @@ package uk.gov.hmcts.reform.preapi.controllers; - import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/controllers/LegacyReportController.java b/src/main/java/uk/gov/hmcts/reform/preapi/controllers/LegacyReportController.java new file mode 100644 index 000000000..1891f97bd --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/preapi/controllers/LegacyReportController.java @@ -0,0 +1,160 @@ +package uk.gov.hmcts.reform.preapi.controllers; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import uk.gov.hmcts.reform.preapi.controllers.params.SearchSharedReport; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.AccessRemovedReportDTO; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.CompletedCaptureSessionReportDTO; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.ConcurrentCaptureSessionReportDTO; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.EditReportDTO; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.PlaybackReportDTO; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.RecordingsPerCaseReportDTO; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.ScheduleReportDTO; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.SharedReportDTO; +import uk.gov.hmcts.reform.preapi.dto.reports.RecordingParticipantsReportDTO; +import uk.gov.hmcts.reform.preapi.dto.reports.UserPrimaryCourtReportDTO; +import uk.gov.hmcts.reform.preapi.enums.AuditLogSource; +import uk.gov.hmcts.reform.preapi.services.LegacyReportService; +import uk.gov.hmcts.reform.preapi.services.ReportService; + +import java.util.List; +import java.util.UUID; + +@RestController +@RequestMapping("/reports") +public class LegacyReportController { + + private final LegacyReportService reportService; + private final ReportService v2ReportService; + + public LegacyReportController(LegacyReportService reportService, ReportService v2ReportService) { + this.reportService = reportService; + this.v2ReportService = v2ReportService; + } + + @GetMapping("/capture-sessions-concurrent") + @Operation(operationId = "reportConcurrentCaptureSessions") + public ResponseEntity> reportConcurrentCaptureSessions() { + return ResponseEntity.ok(reportService.reportCaptureSessions()); + } + + @GetMapping("/recordings-per-case") + @Operation( + operationId = "reportRecordingsPerCase", + summary = "Get the number of completed capture sessions for each case" + ) + public ResponseEntity> reportRecordingsPerCase() { + return ResponseEntity.ok(reportService.reportRecordingsPerCase()); + } + + @GetMapping("/edits") + @Operation(operationId = "reportEdits", summary = "Get a report on recordings edits") + public ResponseEntity> reportEdits() { + return ResponseEntity.ok(reportService.reportEdits()); + } + + @GetMapping("/shared-bookings") + @Operation(operationId = "reportBookingsShared", summary = "Get a report on the bookings that have been shared") + @Parameter( + name = "courtId", + description = "The court id to search by", + schema = @Schema(implementation = UUID.class), + example = "123e4567-e89b-12d3-a456-426614174000" + ) + @Parameter( + name = "bookingId", + description = "The booking id to search by", + schema = @Schema(implementation = UUID.class), + example = "123e4567-e89b-12d3-a456-426614174000" + ) + @Parameter( + name = "sharedWithId", + description = "The id of the user the booking is shared with to search by", + schema = @Schema(implementation = UUID.class), + example = "123e4567-e89b-12d3-a456-426614174000" + ) + @Parameter( + name = "sharedWithEmail", + description = "The email of the user the booking is shared with to search by", + schema = @Schema(implementation = String.class), + example = "example@example.com" + ) + public ResponseEntity> reportBookingsShared( + @Parameter(hidden = true) SearchSharedReport params + ) { + return ResponseEntity.ok(reportService.reportShared( + params.getCourtId(), + params.getBookingId(), + params.getSharedWithId(), + params.getSharedWithEmail() + )); + } + + @GetMapping("/schedules") + @Operation( + operationId = "reportSchedules", + summary = "Get a list of completed capture sessions with booking details" + ) + public ResponseEntity> reportSchedules() { + return ResponseEntity.ok(reportService.reportScheduled()); + } + + + @GetMapping("/playback") + @Operation( + operationId = "reportPlayback", + summary = "Get report on playback by playback source (PORTAL, APPLICATION)" + ) + @Parameter( + name = "source", + description = "The source of the playback. Only accepts PORTAL, APPLICATION or null", + schema = @Schema(implementation = AuditLogSource.class) + ) + public ResponseEntity> reportPlayback( + @RequestParam(required = false) AuditLogSource source + ) { + return ResponseEntity.ok(reportService.reportPlayback(source)); + } + + @GetMapping("/completed-capture-sessions") + @Operation( + operationId = "reportCompletedCaptureSessions", + summary = "Get a report on capture sessions with available recordings" + ) + public ResponseEntity> reportCompletedCaptureSessions() { + return ResponseEntity.ok(reportService.reportCompletedCaptureSessions()); + } + + @GetMapping("/share-bookings-removed") + @Operation( + operationId = "reportShareBookingRemoved", + summary = "Get report on booking share removal" + ) + public ResponseEntity> reportShareBookingRemoved() { + return ResponseEntity.ok(reportService.reportAccessRemoved()); + } + + @GetMapping("/recording-participants") + @Operation( + operationId = "reportRecordingParticipants", + summary = "Get report on participants and the recordings they are part of" + ) + public ResponseEntity> reportRecordingParticipants() { + return ResponseEntity.ok(v2ReportService.reportRecordingParticipants()); + } + + @GetMapping("/user-primary-courts") + @Operation( + operationId = "reportUserPrimaryCourts", + summary = "Get report on app users: their first and last name, their role, their active status, " + + "their primary court and their last access time (if available)") + public ResponseEntity> reportUserPrimaryCourts() { + return ResponseEntity.ok(v2ReportService.reportUserPrimaryCourts()); + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/controllers/ReportController.java b/src/main/java/uk/gov/hmcts/reform/preapi/controllers/ReportController.java index da5ad0584..c06821248 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/controllers/ReportController.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/controllers/ReportController.java @@ -26,7 +26,7 @@ import java.util.UUID; @RestController -@RequestMapping("/reports") +@RequestMapping("/reports-v2") public class ReportController { private final ReportService reportService; @@ -82,6 +82,12 @@ public ResponseEntity> reportEdits() { schema = @Schema(implementation = String.class), example = "example@example.com" ) + @Parameter( + name = "onlyActive", + description = "The shares must be active (not deleted) then true, otherwise false", + schema = @Schema(implementation = Boolean.class), + example = "true" + ) public ResponseEntity> reportBookingsShared( @Parameter(hidden = true) SearchSharedReport params ) { @@ -89,7 +95,8 @@ public ResponseEntity> reportBookingsShared( params.getCourtId(), params.getBookingId(), params.getSharedWithId(), - params.getSharedWithEmail() + params.getSharedWithEmail(), + params.getOnlyActive() != null && params.getOnlyActive() )); } diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/controllers/params/SearchSharedReport.java b/src/main/java/uk/gov/hmcts/reform/preapi/controllers/params/SearchSharedReport.java index c0dcb6a2c..d9b1ea1ee 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/controllers/params/SearchSharedReport.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/controllers/params/SearchSharedReport.java @@ -10,6 +10,7 @@ public class SearchSharedReport { private UUID bookingId; private UUID sharedWithId; private String sharedWithEmail; + private Boolean onlyActive; public String getSharedWithEmail() { return sharedWithEmail != null && !sharedWithEmail.isEmpty() ? sharedWithEmail : null; diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/CourtDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/CourtDTO.java index 2fd84cc6d..3465a7eb1 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/dto/CourtDTO.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/CourtDTO.java @@ -31,6 +31,12 @@ public class CourtDTO { @Schema(description = "CourtLocationCode") private String locationCode; + @Schema(description = "CourtCounty") + private String county; + + @Schema(description = "CourtPostcode") + private String postcode; + @Schema(description = "CourtRegions") private List regions; // this was removed?? @@ -42,6 +48,8 @@ public CourtDTO(Court courtEntity) { this.name = courtEntity.getName(); this.courtType = courtEntity.getCourtType(); this.locationCode = courtEntity.getLocationCode(); + this.county = courtEntity.getCounty(); + this.postcode = courtEntity.getPostcode(); this.regions = Stream.ofNullable(courtEntity.getRegions()) .flatMap(regions -> regions.stream().map(RegionDTO::new)) .sorted(Comparator.comparing(RegionDTO::getName)) diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/CreateCourtDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/CreateCourtDTO.java index 8281e654a..66978760d 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/dto/CreateCourtDTO.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/CreateCourtDTO.java @@ -1,10 +1,10 @@ package uk.gov.hmcts.reform.preapi.dto; - import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.annotation.JsonNaming; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; import lombok.Data; import lombok.NoArgsConstructor; @@ -34,6 +34,13 @@ public class CreateCourtDTO { @NotNull(message = "location_code is required") private String locationCode; + @Schema(description = "CreateCourtCounty") + private String county; + + @Schema(description = "CreateCourtPostcode") + @Pattern(regexp = "^[A-Z]{1,2}[0-9][0-9A-Z]? [0-9][A-Z]{2}$", message = "invalid postcode") + private String postcode; + @Schema(description = "CreateCourtRegionIds") @Size(min = 1, message = "must contain at least 1") @NotNull(message = "regions is required and must contain at least 1") diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/AccessRemovedReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/AccessRemovedReportDTO.java new file mode 100644 index 000000000..c18fe4309 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/AccessRemovedReportDTO.java @@ -0,0 +1,58 @@ +package uk.gov.hmcts.reform.preapi.dto.legacyreports; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import uk.gov.hmcts.reform.preapi.dto.RegionDTO; +import uk.gov.hmcts.reform.preapi.entities.ShareBooking; + +import java.sql.Timestamp; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Data +@NoArgsConstructor +@Schema(description = "AccessRemovedReportDTO") +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class AccessRemovedReportDTO { + + @Schema(description = "AccessRemovedReportRemovedAt") + private Timestamp removedAt; + + @Schema(description = "AccessRemovedReportCaseReference") + private String caseReference; + + @Schema(description = "AccessRemovedReportCourtName") + private String court; + + @Schema(description = "AccessRemovedReportRegions") + private Set regions; + + @Schema(description = "AccessRemovedReportUserFullName") + private String userFullName; + + @Schema(description = "AccessRemovedReportUserEmail") + private String userEmail; + + @Schema(description = "AccessRemovedReportRemovalReason") + private String removalReason; + + public AccessRemovedReportDTO(ShareBooking shareBooking) { + var booking = shareBooking.getBooking(); + var courtEntity = booking.getCaseId().getCourt(); + + removedAt = shareBooking.getDeletedAt(); + caseReference = booking.getCaseId().getReference(); + court = courtEntity.getName(); + regions = Stream.ofNullable(courtEntity.getRegions()) + .flatMap(regions -> regions.stream().map(RegionDTO::new)) + .collect(Collectors.toSet()); + + var user = shareBooking.getSharedWith(); + userFullName = user.getFullName(); + userEmail = user.getEmail(); + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/CompletedCaptureSessionReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/CompletedCaptureSessionReportDTO.java new file mode 100644 index 000000000..ceee1e7c9 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/CompletedCaptureSessionReportDTO.java @@ -0,0 +1,88 @@ +package uk.gov.hmcts.reform.preapi.dto.legacyreports; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import uk.gov.hmcts.reform.preapi.dto.RegionDTO; +import uk.gov.hmcts.reform.preapi.entities.Recording; +import uk.gov.hmcts.reform.preapi.enums.ParticipantType; +import uk.gov.hmcts.reform.preapi.enums.RecordingStatus; + +import java.sql.Timestamp; +import java.time.Duration; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Data +@NoArgsConstructor +@Schema(description = "CompletedCaptureSessionReportDTO") +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class CompletedCaptureSessionReportDTO { + + @Schema(description = "CompletedCaptureSessionReportStartedAt") + private Timestamp startedAt; + + @Schema(description = "CompletedCaptureSessionReportFinishedAt") + private Timestamp finishedAt; + + @Schema( + description = "CompletedCaptureSessionReportDuration", + implementation = String.class + ) + @JsonFormat(shape = JsonFormat.Shape.STRING) + private Duration duration; + + @Schema(description = "CompletedCaptureSessionReportBookingScheduledFor") + private Timestamp scheduledFor; + + @Schema(description = "CompletedCaptureSessionReportCaseReference") + private String caseReference; + + @Schema(description = "CompletedCaptureSessionReportDefendantCount") + private int countDefendants; + + @Schema(description = "CompletedCaptureSessionReportWitnessCount") + private int countWitnesses; + + @Schema(description = "CompletedCaptureSessionReportRecordingStatus") + private RecordingStatus recordingStatus; + + @Schema(description = "CompletedCaptureSessionReportCourtName") + private String court; + + @Schema(description = "CompletedCaptureSessionReportRegions") + private Set regions; + + public CompletedCaptureSessionReportDTO(Recording recording) { + var captureSession = recording.getCaptureSession(); + + startedAt = captureSession.getStartedAt(); + finishedAt = captureSession.getFinishedAt(); + duration = recording.getDuration(); + + var booking = captureSession.getBooking(); + scheduledFor = booking.getScheduledFor(); + + var caseEntity = booking.getCaseId(); + caseReference = caseEntity.getReference(); + countDefendants = (int) booking + .getParticipants() + .stream() + .filter(p -> p.getParticipantType() == ParticipantType.DEFENDANT) + .count(); + countWitnesses = (int) booking + .getParticipants() + .stream() + .filter(p -> p.getParticipantType() == ParticipantType.WITNESS) + .count(); + recordingStatus = captureSession.getStatus(); + court = caseEntity.getCourt().getName(); + regions = Stream.ofNullable(caseEntity.getCourt().getRegions()) + .flatMap(regions -> regions.stream().map(RegionDTO::new)) + .collect(Collectors.toSet()); + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/ConcurrentCaptureSessionReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/ConcurrentCaptureSessionReportDTO.java new file mode 100644 index 000000000..d18c0ec62 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/ConcurrentCaptureSessionReportDTO.java @@ -0,0 +1,67 @@ +package uk.gov.hmcts.reform.preapi.dto.legacyreports; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import uk.gov.hmcts.reform.preapi.dto.RegionDTO; +import uk.gov.hmcts.reform.preapi.entities.CaptureSession; + +import java.sql.Timestamp; +import java.time.Duration; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + + +@Data +@NoArgsConstructor +@Schema(description = "ConcurrentCaptureSessionReportDTO") +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class ConcurrentCaptureSessionReportDTO { + + @Schema(description = "CaptureSessionId") + private UUID id; + + @Schema(description = "CaptureSessionStartTime") + private Timestamp startTime; + + @Schema(description = "CaptureSessionEndTime") + private Timestamp endTime; + + @Schema(description = "CaptureSessionDuration", implementation = String.class) + @JsonFormat(shape = JsonFormat.Shape.STRING) + private Duration duration; + + @Schema(description = "CaptureSessionCaseReference") + private String caseReference; + + @Schema(description = "CaptureSessionCourtName") + private String court; + + @Schema(description = "CaptureSessionRegionName") + private Set region; + + + public ConcurrentCaptureSessionReportDTO(CaptureSession entity) { + id = entity.getId(); + startTime = entity.getStartedAt(); + endTime = entity.getFinishedAt(); + var caseEntity = entity.getBooking().getCaseId(); + var courtEntity = caseEntity.getCourt(); + court = courtEntity.getName(); + caseReference = caseEntity.getReference(); + region = Stream.ofNullable(caseEntity.getCourt().getRegions()) + .flatMap(regions -> regions.stream().map(RegionDTO::new)) + .collect(Collectors.toSet()); + + Stream.ofNullable(entity.getRecordings()) + .flatMap(Set::stream) + .filter(r -> r.getVersion() == 1 && !r.isDeleted()) + .findFirst() + .ifPresent(r -> duration = r.getDuration()); + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/EditReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/EditReportDTO.java new file mode 100644 index 000000000..fa68e555d --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/EditReportDTO.java @@ -0,0 +1,56 @@ +package uk.gov.hmcts.reform.preapi.dto.legacyreports; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import uk.gov.hmcts.reform.preapi.dto.RegionDTO; +import uk.gov.hmcts.reform.preapi.entities.Recording; + +import java.sql.Timestamp; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + + +@Data +@NoArgsConstructor +@Schema(description = "EditReportDTO") +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class EditReportDTO { + + @Schema(description = "EditReportEditCreatedAt") + private Timestamp createdAt; + + @Schema(description = "EditReportRecordingVersion") + private int version; + + @Schema(description = "EditReportCaseReference") + private String caseReference; + + @Schema(description = "EditReportCourtName") + private String court; + + @Schema(description = "EditReportRegions") + private Set regions; + + @Schema(description = "EditReportRecordingId") + private UUID recordingId; + + public EditReportDTO(Recording recordingEntity) { + createdAt = recordingEntity.getCreatedAt(); + version = recordingEntity.getVersion(); + var caseEntity = recordingEntity + .getCaptureSession() + .getBooking() + .getCaseId(); + caseReference = caseEntity.getReference(); + court = caseEntity.getCourt().getName(); + regions = Stream.ofNullable(caseEntity.getCourt().getRegions()) + .flatMap(regions -> regions.stream().map(RegionDTO::new)) + .collect(Collectors.toSet()); + recordingId = recordingEntity.getId(); + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/PlaybackReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/PlaybackReportDTO.java new file mode 100644 index 000000000..2268a5e59 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/PlaybackReportDTO.java @@ -0,0 +1,66 @@ +package uk.gov.hmcts.reform.preapi.dto.legacyreports; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import uk.gov.hmcts.reform.preapi.dto.RegionDTO; +import uk.gov.hmcts.reform.preapi.entities.Audit; +import uk.gov.hmcts.reform.preapi.entities.Recording; +import uk.gov.hmcts.reform.preapi.entities.User; + +import java.sql.Timestamp; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.annotation.Nullable; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "PlaybackReportDTO") +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class PlaybackReportDTO { + + @Schema(description = "PlaybackReportPlaybackAt") + private Timestamp playbackAt; + + @Schema(description = "PlaybackReportUserFullName") + private String userFullName; + + @Schema(description = "PlaybackReportUserEmail") + private String userEmail; + + @Schema(description = "PlaybackReportCaseReference") + private String caseReference; + + @Schema(description = "PlaybackReportCourt") + private String court; + + @Schema(description = "PlaybackReportRegions") + private Set regions; + + @Schema(description = "PlaybackReportRecordingId") + private UUID recordingId; + + public PlaybackReportDTO(Audit audit, User user, @Nullable Recording recording) { + playbackAt = audit.getCreatedAt(); + if (user != null) { + userFullName = user.getFullName(); + userEmail = user.getEmail(); + } + if (recording != null) { + var caseEntity = recording.getCaptureSession().getBooking().getCaseId(); + var courtEntity = caseEntity.getCourt(); + court = courtEntity.getName(); + caseReference = caseEntity.getReference(); + regions = Stream.ofNullable(caseEntity.getCourt().getRegions()) + .flatMap(regions -> regions.stream().map(RegionDTO::new)) + .collect(Collectors.toSet()); + recordingId = recording.getId(); + } + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/RecordingsPerCaseReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/RecordingsPerCaseReportDTO.java new file mode 100644 index 000000000..e227bb823 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/RecordingsPerCaseReportDTO.java @@ -0,0 +1,41 @@ +package uk.gov.hmcts.reform.preapi.dto.legacyreports; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import uk.gov.hmcts.reform.preapi.dto.RegionDTO; +import uk.gov.hmcts.reform.preapi.entities.Case; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Data +@NoArgsConstructor +@Schema(description = "RecordingsPerCaseReportDTO") +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class RecordingsPerCaseReportDTO { + + @Schema(description = "RecordingsPerCaseCaseReference") + private String caseReference; + + @Schema(description = "RecordingsPerCaseCourt") + private String court; + + @Schema(description = "RecordingsPerCaseRegions") + private Set regions; + + @Schema(description = "RecordingsPerCaseCount") + private int count; + + public RecordingsPerCaseReportDTO(Case caseEntity, int count) { + caseReference = caseEntity.getReference(); + court = caseEntity.getCourt().getName(); + regions = Stream.ofNullable(caseEntity.getCourt().getRegions()) + .flatMap(regions -> regions.stream().map(RegionDTO::new)) + .collect(Collectors.toSet()); + this.count = count; + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/ScheduleReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/ScheduleReportDTO.java new file mode 100644 index 000000000..454320168 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/ScheduleReportDTO.java @@ -0,0 +1,54 @@ +package uk.gov.hmcts.reform.preapi.dto.legacyreports; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import uk.gov.hmcts.reform.preapi.dto.RegionDTO; +import uk.gov.hmcts.reform.preapi.entities.CaptureSession; + +import java.sql.Timestamp; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Data +@NoArgsConstructor +@Schema(description = "ScheduleReportDTO") +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class ScheduleReportDTO { + + @Schema(description = "ScheduleReportStartedAt") + private Timestamp scheduledFor; + + @Schema(description = "ScheduleReportBookingCreatedAt") + private Timestamp bookingCreatedAt; + + @Schema(description = "ScheduleReportCaseReference") + private String caseReference; + + @Schema(description = "ScheduleReportUserEmail") + private String captureSessionUser; + + @Schema(description = "ScheduleReportCourtName") + private String court; + + @Schema(description = "ScheduleReportCourtRegions") + private Set regions; + + public ScheduleReportDTO(CaptureSession captureSession) { + var bookingEntity = captureSession.getBooking(); + var caseEntity = bookingEntity.getCaseId(); + scheduledFor = bookingEntity.getScheduledFor(); + bookingCreatedAt = bookingEntity.getCreatedAt(); + caseReference = caseEntity.getReference(); + if (captureSession.getStartedByUser() != null) { + captureSessionUser = captureSession.getStartedByUser().getEmail(); + } + court = caseEntity.getCourt().getName(); + regions = Stream.ofNullable(caseEntity.getCourt().getRegions()) + .flatMap(regions -> regions.stream().map(RegionDTO::new)) + .collect(Collectors.toSet()); + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/SharedReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/SharedReportDTO.java new file mode 100644 index 000000000..1161a82e3 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/legacyreports/SharedReportDTO.java @@ -0,0 +1,65 @@ +package uk.gov.hmcts.reform.preapi.dto.legacyreports; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import uk.gov.hmcts.reform.preapi.dto.RegionDTO; +import uk.gov.hmcts.reform.preapi.entities.ShareBooking; + +import java.sql.Timestamp; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + + +@Data +@NoArgsConstructor +@Schema(description = "SharedReportDTO") +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class SharedReportDTO { + + @Schema(description = "SharedReportSharedAt") + private Timestamp sharedAt; + + @Schema(description = "SharedReportAllocatedTo") + private String allocatedTo; + + @Schema(description = "SharedReportAllocatedToFullName") + private String allocatedToFullName; + + @Schema(description = "SharedReportAllocatedBy") + private String allocatedBy; + + @Schema(description = "SharedReportAllocatedToFullName") + private String allocatedByFullName; + + @Schema(description = "SharedReportCaseReference") + private String caseReference; + + @Schema(description = "SharedReportCourtName") + private String court; + + @Schema(description = "SharedReportRegions") + private Set regions; + + @Schema(description = "SharedReportBookingId") + private UUID bookingId; + + public SharedReportDTO(ShareBooking shareBooking) { + sharedAt = shareBooking.getCreatedAt(); + allocatedTo = shareBooking.getSharedWith().getEmail(); + allocatedToFullName = shareBooking.getSharedWith().getFullName(); + allocatedBy = shareBooking.getSharedBy().getEmail(); + allocatedByFullName = shareBooking.getSharedBy().getFullName(); + var caseEntity = shareBooking.getBooking().getCaseId(); + caseReference = caseEntity.getReference(); + court = caseEntity.getCourt().getName(); + regions = Stream.ofNullable(caseEntity.getCourt().getRegions()) + .flatMap(regions -> regions.stream().map(RegionDTO::new)) + .collect(Collectors.toSet()); + bookingId = shareBooking.getBooking().getId(); + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/AccessRemovedReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/AccessRemovedReportDTO.java index b334d68e0..7302d6839 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/AccessRemovedReportDTO.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/AccessRemovedReportDTO.java @@ -4,55 +4,41 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import uk.gov.hmcts.reform.preapi.dto.RegionDTO; import uk.gov.hmcts.reform.preapi.entities.ShareBooking; - -import java.sql.Timestamp; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import uk.gov.hmcts.reform.preapi.utils.DateTimeUtils; @Data @NoArgsConstructor +@EqualsAndHashCode(callSuper = true) @Schema(description = "AccessRemovedReportDTO") @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -public class AccessRemovedReportDTO { - - @Schema(description = "AccessRemovedReportRemovedAt") - private Timestamp removedAt; +public class AccessRemovedReportDTO extends BaseReportDTO { - @Schema(description = "AccessRemovedReportCaseReference") - private String caseReference; + @Schema(description = "AccessRemovedReportRemovedDate") + private String removedDate; - @Schema(description = "AccessRemovedReportCourtName") - private String court; + @Schema(description = "AccessRemovedReportRemovedTime") + private String removedTime; - @Schema(description = "AccessRemovedReportRegions") - private Set regions; + @Schema(description = "AccessRemovedReportRemovedTimezone") + private String removedTimezone; @Schema(description = "AccessRemovedReportUserFullName") - private String userFullName; + private String fullName; @Schema(description = "AccessRemovedReportUserEmail") private String userEmail; - @Schema(description = "AccessRemovedReportRemovalReason") - private String removalReason; - public AccessRemovedReportDTO(ShareBooking shareBooking) { - var booking = shareBooking.getBooking(); - var courtEntity = booking.getCaseId().getCourt(); - - removedAt = shareBooking.getDeletedAt(); - caseReference = booking.getCaseId().getReference(); - court = courtEntity.getName(); - regions = Stream.ofNullable(courtEntity.getRegions()) - .flatMap(regions -> regions.stream().map(RegionDTO::new)) - .collect(Collectors.toSet()); + super(shareBooking.getBooking().getCaseId()); + removedDate = DateTimeUtils.formatDate(shareBooking.getDeletedAt()); + removedTime = DateTimeUtils.formatTime(shareBooking.getDeletedAt()); + removedTimezone = DateTimeUtils.getTimezoneAbbreviation(shareBooking.getDeletedAt()); var user = shareBooking.getSharedWith(); - userFullName = user.getFullName(); + fullName = user.getFullName(); userEmail = user.getEmail(); } } diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/BaseReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/BaseReportDTO.java new file mode 100644 index 000000000..008d36053 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/BaseReportDTO.java @@ -0,0 +1,40 @@ +package uk.gov.hmcts.reform.preapi.dto.reports; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import uk.gov.hmcts.reform.preapi.entities.Case; +import uk.gov.hmcts.reform.preapi.entities.Court; +import uk.gov.hmcts.reform.preapi.entities.Region; + +@Data +@NoArgsConstructor +public abstract class BaseReportDTO { + @Schema(description = "ReportCaseReference") + private String caseReference; + + @Schema(description = "ReportCourt") + private String court; + + @Schema(description = "ReportCounty") + private String county; + + @Schema(description = "ReportPostcode") + private String postcode; + + @Schema(description = "ReportRegion") + private String region; + + protected BaseReportDTO(Case c) { + if (c == null) { + return; + } + + caseReference = c.getReference(); + Court courtEntity = c.getCourt(); + court = courtEntity.getName(); + county = courtEntity.getCounty(); + postcode = courtEntity.getPostcode(); + region = courtEntity.getRegions().stream().findFirst().map(Region::getName).orElse(null); + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/CompletedCaptureSessionReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/CompletedCaptureSessionReportDTO.java index 73261f238..b1efe153b 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/CompletedCaptureSessionReportDTO.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/CompletedCaptureSessionReportDTO.java @@ -1,88 +1,84 @@ package uk.gov.hmcts.reform.preapi.dto.reports; -import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.annotation.JsonNaming; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import uk.gov.hmcts.reform.preapi.dto.RegionDTO; +import uk.gov.hmcts.reform.preapi.entities.Participant; import uk.gov.hmcts.reform.preapi.entities.Recording; import uk.gov.hmcts.reform.preapi.enums.ParticipantType; import uk.gov.hmcts.reform.preapi.enums.RecordingStatus; +import uk.gov.hmcts.reform.preapi.utils.DateTimeUtils; -import java.sql.Timestamp; -import java.time.Duration; -import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; @Data @NoArgsConstructor +@EqualsAndHashCode(callSuper = true) @Schema(description = "CompletedCaptureSessionReportDTO") @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -public class CompletedCaptureSessionReportDTO { +public class CompletedCaptureSessionReportDTO extends BaseReportDTO { - @Schema(description = "CompletedCaptureSessionReportStartedAt") - private Timestamp startedAt; + @Schema(description = "CompletedCaptureSessionReportRecordingDate") + private String recordingDate; - @Schema(description = "CompletedCaptureSessionReportFinishedAt") - private Timestamp finishedAt; + @Schema(description = "CompletedCaptureSessionReportRecordingTime") + private String recordingTime; - @Schema( - description = "CompletedCaptureSessionReportDuration", - implementation = String.class - ) - @JsonFormat(shape = JsonFormat.Shape.STRING) - private Duration duration; + @Schema(description = "CompletedCaptureSessionReportFinishTime") + private String finishTime; - @Schema(description = "CompletedCaptureSessionReportBookingScheduledFor") - private Timestamp scheduledFor; + @Schema(description = "CompletedCaptureSessionReportTimezone") + private String timezone; - @Schema(description = "CompletedCaptureSessionReportCaseReference") - private String caseReference; + @Schema(description = "CompletedCaptureSessionReportScheduledDate") + private String scheduledDate; - @Schema(description = "CompletedCaptureSessionReportDefendantCount") - private int countDefendants; + @Schema(description = "CompletedCaptureSessionReportStatus") + private RecordingStatus status; - @Schema(description = "CompletedCaptureSessionReportWitnessCount") - private int countWitnesses; + @Schema(description = "CompletedCaptureSessionReportDefendantNames") + private String defendantNames; - @Schema(description = "CompletedCaptureSessionReportRecordingStatus") - private RecordingStatus recordingStatus; + @Schema(description = "CompletedCaptureSessionReportDefendantCount") + private int defendant; - @Schema(description = "CompletedCaptureSessionReportCourtName") - private String court; + @Schema(description = "CompletedCaptureSessionReportWitnessNames") + private String witnessNames; - @Schema(description = "CompletedCaptureSessionReportRegions") - private Set regions; + @Schema(description = "CompletedCaptureSessionReportWitnessCount") + private int witness; public CompletedCaptureSessionReportDTO(Recording recording) { + super(recording.getCaptureSession().getBooking().getCaseId()); var captureSession = recording.getCaptureSession(); + status = captureSession.getStatus(); - startedAt = captureSession.getStartedAt(); - finishedAt = captureSession.getFinishedAt(); - duration = recording.getDuration(); - + recordingDate = DateTimeUtils.formatDate(captureSession.getStartedAt()); + recordingTime = DateTimeUtils.formatTime(captureSession.getStartedAt()); + finishTime = DateTimeUtils.formatTime(captureSession.getFinishedAt()); + timezone = DateTimeUtils.getTimezoneAbbreviation(captureSession.getStartedAt()); var booking = captureSession.getBooking(); - scheduledFor = booking.getScheduledFor(); + scheduledDate = DateTimeUtils.formatDate(captureSession.getBooking().getScheduledFor()); - var caseEntity = booking.getCaseId(); - caseReference = caseEntity.getReference(); - countDefendants = (int) booking - .getParticipants() + var defendants = booking.getParticipants() .stream() .filter(p -> p.getParticipantType() == ParticipantType.DEFENDANT) - .count(); - countWitnesses = (int) booking - .getParticipants() + .toList(); + defendantNames = defendants.stream() + .map(Participant::getFullName) + .collect(Collectors.joining(", ")); + defendant = defendants.size(); + + var witnesses = booking.getParticipants() .stream() .filter(p -> p.getParticipantType() == ParticipantType.WITNESS) - .count(); - recordingStatus = captureSession.getStatus(); - court = caseEntity.getCourt().getName(); - regions = Stream.ofNullable(caseEntity.getCourt().getRegions()) - .flatMap(regions -> regions.stream().map(RegionDTO::new)) - .collect(Collectors.toSet()); + .toList(); + witnessNames = witnesses.stream() + .map(Participant::getFullName) + .collect(Collectors.joining(", ")); + witness = witnesses.size(); } } diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/ConcurrentCaptureSessionReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/ConcurrentCaptureSessionReportDTO.java index f64d9cda3..f66b0b02b 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/ConcurrentCaptureSessionReportDTO.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/ConcurrentCaptureSessionReportDTO.java @@ -1,62 +1,64 @@ package uk.gov.hmcts.reform.preapi.dto.reports; -import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.annotation.JsonNaming; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import uk.gov.hmcts.reform.preapi.dto.RegionDTO; import uk.gov.hmcts.reform.preapi.entities.CaptureSession; +import uk.gov.hmcts.reform.preapi.entities.Recording; +import uk.gov.hmcts.reform.preapi.utils.DateTimeUtils; -import java.sql.Timestamp; import java.time.Duration; import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; import java.util.stream.Stream; - @Data @NoArgsConstructor +@EqualsAndHashCode(callSuper = true) @Schema(description = "ConcurrentCaptureSessionReportDTO") @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -public class ConcurrentCaptureSessionReportDTO { - - @Schema(description = "CaptureSessionId") - private UUID id; +public class ConcurrentCaptureSessionReportDTO extends BaseReportDTO { + @Schema(description = "CaptureSessionStartDate") + private String date; @Schema(description = "CaptureSessionStartTime") - private Timestamp startTime; + private String startTime; @Schema(description = "CaptureSessionEndTime") - private Timestamp endTime; - - @Schema(description = "CaptureSessionDuration", implementation = String.class) - @JsonFormat(shape = JsonFormat.Shape.STRING) - private Duration duration; + private String endTime; - @Schema(description = "CaptureSessionCaseReference") - private String caseReference; + @Schema(description = "CaptureSessionStartTimezone") + private String timezone; - @Schema(description = "CaptureSessionCourtName") - private String court; - - @Schema(description = "CaptureSessionRegionName") - private Set region; + @JsonIgnore + private Duration duration; + @JsonProperty("duration") + @Schema(description = "CaptureSessionDuration", implementation = String.class) + public String getDurationAsString() { + if (duration == null) { + return null; + } + return String.format("%02d:%02d:%02d", + duration.toHoursPart(), + duration.toMinutesPart(), + duration.toSecondsPart()); + } public ConcurrentCaptureSessionReportDTO(CaptureSession entity) { - id = entity.getId(); - startTime = entity.getStartedAt(); - endTime = entity.getFinishedAt(); - var caseEntity = entity.getBooking().getCaseId(); - var courtEntity = caseEntity.getCourt(); - court = courtEntity.getName(); - caseReference = caseEntity.getReference(); - region = Stream.ofNullable(caseEntity.getCourt().getRegions()) - .flatMap(regions -> regions.stream().map(RegionDTO::new)) - .collect(Collectors.toSet()); + super(entity.getBooking().getCaseId()); + date = DateTimeUtils.formatDate(entity.getStartedAt()); + timezone = DateTimeUtils.getTimezoneAbbreviation(entity.getStartedAt()); + startTime = DateTimeUtils.formatTime(entity.getStartedAt()); + + if (entity.getFinishedAt() != null) { + endTime = DateTimeUtils.formatTime(entity.getFinishedAt()); + duration = entity.getRecordings().stream().findFirst().map(Recording::getDuration).orElse(null); + } Stream.ofNullable(entity.getRecordings()) .flatMap(Set::stream) diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/EditReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/EditReportDTO.java index 9fa41e82a..8604f985b 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/EditReportDTO.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/EditReportDTO.java @@ -4,53 +4,35 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import uk.gov.hmcts.reform.preapi.dto.RegionDTO; import uk.gov.hmcts.reform.preapi.entities.Recording; - -import java.sql.Timestamp; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; -import java.util.stream.Stream; - +import uk.gov.hmcts.reform.preapi.utils.DateTimeUtils; @Data @NoArgsConstructor +@EqualsAndHashCode(callSuper = true) @Schema(description = "EditReportDTO") @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -public class EditReportDTO { - - @Schema(description = "EditReportEditCreatedAt") - private Timestamp createdAt; - - @Schema(description = "EditReportRecordingVersion") - private int version; +public class EditReportDTO extends BaseReportDTO { - @Schema(description = "EditReportCaseReference") - private String caseReference; + @Schema(description = "EditReportEditDate") + private String editDate; - @Schema(description = "EditReportCourtName") - private String court; + @Schema(description = "EditReportEditTime") + private String editTime; - @Schema(description = "EditReportRegions") - private Set regions; + @Schema(description = "EditReportEditTimezone") + private String timezone; - @Schema(description = "EditReportRecordingId") - private UUID recordingId; + @Schema(description = "EditReportRecordingVersion") + private int version; public EditReportDTO(Recording recordingEntity) { - createdAt = recordingEntity.getCreatedAt(); + super(recordingEntity.getCaptureSession().getBooking().getCaseId()); + editDate = DateTimeUtils.formatDate(recordingEntity.getCreatedAt()); + editTime = DateTimeUtils.formatTime(recordingEntity.getCreatedAt()); + timezone = DateTimeUtils.getTimezoneAbbreviation(recordingEntity.getCreatedAt()); version = recordingEntity.getVersion(); - var caseEntity = recordingEntity - .getCaptureSession() - .getBooking() - .getCaseId(); - caseReference = caseEntity.getReference(); - court = caseEntity.getCourt().getName(); - regions = Stream.ofNullable(caseEntity.getCourt().getRegions()) - .flatMap(regions -> regions.stream().map(RegionDTO::new)) - .collect(Collectors.toSet()); - recordingId = recordingEntity.getId(); } } diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/PlaybackReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/PlaybackReportDTO.java index 28e2259b4..464049e53 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/PlaybackReportDTO.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/PlaybackReportDTO.java @@ -5,28 +5,31 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import uk.gov.hmcts.reform.preapi.dto.RegionDTO; import uk.gov.hmcts.reform.preapi.entities.Audit; import uk.gov.hmcts.reform.preapi.entities.Recording; import uk.gov.hmcts.reform.preapi.entities.User; +import uk.gov.hmcts.reform.preapi.utils.DateTimeUtils; -import java.sql.Timestamp; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.annotation.Nullable; @Data @NoArgsConstructor @AllArgsConstructor +@EqualsAndHashCode(callSuper = true) @Schema(description = "PlaybackReportDTO") @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -public class PlaybackReportDTO { +public class PlaybackReportDTO extends BaseReportDTO { - @Schema(description = "PlaybackReportPlaybackAt") - private Timestamp playbackAt; + @Schema(description = "PlaybackReportPlaybackDate") + private String playbackDate; + + @Schema(description = "PlaybackReportPlaybackTime") + private String playbackTime; + + @Schema(description = "PlaybackReportTimeZone") + private String playbackTimeZone; @Schema(description = "PlaybackReportUserFullName") private String userFullName; @@ -34,33 +37,19 @@ public class PlaybackReportDTO { @Schema(description = "PlaybackReportUserEmail") private String userEmail; - @Schema(description = "PlaybackReportCaseReference") - private String caseReference; - - @Schema(description = "PlaybackReportCourt") - private String court; - - @Schema(description = "PlaybackReportRegions") - private Set regions; - - @Schema(description = "PlaybackReportRecordingId") - private UUID recordingId; + @Schema(description = "PlaybackReportUserOrganisation") + private String userOrganisation; public PlaybackReportDTO(Audit audit, User user, @Nullable Recording recording) { - playbackAt = audit.getCreatedAt(); + super(recording != null ? recording.getCaptureSession().getBooking().getCaseId() : null); + + playbackDate = DateTimeUtils.formatDate(audit.getCreatedAt()); + playbackTime = DateTimeUtils.formatTime(audit.getCreatedAt()); + playbackTimeZone = DateTimeUtils.getTimezoneAbbreviation(audit.getCreatedAt()); if (user != null) { userFullName = user.getFullName(); userEmail = user.getEmail(); - } - if (recording != null) { - var caseEntity = recording.getCaptureSession().getBooking().getCaseId(); - var courtEntity = caseEntity.getCourt(); - court = courtEntity.getName(); - caseReference = caseEntity.getReference(); - regions = Stream.ofNullable(caseEntity.getCourt().getRegions()) - .flatMap(regions -> regions.stream().map(RegionDTO::new)) - .collect(Collectors.toSet()); - recordingId = recording.getId(); + userOrganisation = user.getOrganisation(); } } } diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/RecordingsPerCaseReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/RecordingsPerCaseReportDTO.java index cb4b254da..cbfde8a1d 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/RecordingsPerCaseReportDTO.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/RecordingsPerCaseReportDTO.java @@ -4,38 +4,22 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import uk.gov.hmcts.reform.preapi.dto.RegionDTO; import uk.gov.hmcts.reform.preapi.entities.Case; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - @Data @NoArgsConstructor +@EqualsAndHashCode(callSuper = true) @Schema(description = "RecordingsPerCaseReportDTO") @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -public class RecordingsPerCaseReportDTO { - - @Schema(description = "RecordingsPerCaseCaseReference") - private String caseReference; - - @Schema(description = "RecordingsPerCaseCourt") - private String court; - - @Schema(description = "RecordingsPerCaseRegions") - private Set regions; +public class RecordingsPerCaseReportDTO extends BaseReportDTO { - @Schema(description = "RecordingsPerCaseCount") - private int count; + @Schema(description = "RecordingsPerCaseNumberOfRecordings") + private int numberOfRecordings; public RecordingsPerCaseReportDTO(Case caseEntity, int count) { - caseReference = caseEntity.getReference(); - court = caseEntity.getCourt().getName(); - regions = Stream.ofNullable(caseEntity.getCourt().getRegions()) - .flatMap(regions -> regions.stream().map(RegionDTO::new)) - .collect(Collectors.toSet()); - this.count = count; + super(caseEntity); + this.numberOfRecordings = count; } } diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/ScheduleReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/ScheduleReportDTO.java index 003d5d1e2..cb45c29e2 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/ScheduleReportDTO.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/ScheduleReportDTO.java @@ -4,51 +4,35 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import uk.gov.hmcts.reform.preapi.dto.RegionDTO; import uk.gov.hmcts.reform.preapi.entities.CaptureSession; - -import java.sql.Timestamp; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import uk.gov.hmcts.reform.preapi.utils.DateTimeUtils; @Data @NoArgsConstructor +@EqualsAndHashCode(callSuper = true) @Schema(description = "ScheduleReportDTO") @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -public class ScheduleReportDTO { +public class ScheduleReportDTO extends BaseReportDTO { - @Schema(description = "ScheduleReportStartedAt") - private Timestamp scheduledFor; + @Schema(description = "ScheduleReportStartedDate") + private String scheduledDate; @Schema(description = "ScheduleReportBookingCreatedAt") - private Timestamp bookingCreatedAt; - - @Schema(description = "ScheduleReportCaseReference") - private String caseReference; + private String dateOfBooking; @Schema(description = "ScheduleReportUserEmail") - private String captureSessionUser; - - @Schema(description = "ScheduleReportCourtName") - private String court; - - @Schema(description = "ScheduleReportCourtRegions") - private Set regions; + private String user; public ScheduleReportDTO(CaptureSession captureSession) { - var bookingEntity = captureSession.getBooking(); - var caseEntity = bookingEntity.getCaseId(); - scheduledFor = bookingEntity.getScheduledFor(); - bookingCreatedAt = bookingEntity.getCreatedAt(); - caseReference = caseEntity.getReference(); + super(captureSession.getBooking().getCaseId()); + var booking = captureSession.getBooking(); + scheduledDate = DateTimeUtils.formatDate(booking.getScheduledFor()); + dateOfBooking = DateTimeUtils.formatDate(booking.getCreatedAt()); + if (captureSession.getStartedByUser() != null) { - captureSessionUser = captureSession.getStartedByUser().getEmail(); + user = captureSession.getStartedByUser().getEmail(); } - court = caseEntity.getCourt().getName(); - regions = Stream.ofNullable(caseEntity.getCourt().getRegions()) - .flatMap(regions -> regions.stream().map(RegionDTO::new)) - .collect(Collectors.toSet()); } } diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/SharedReportDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/SharedReportDTO.java index ec96cd234..0cf683e21 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/SharedReportDTO.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/reports/SharedReportDTO.java @@ -4,62 +4,53 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import uk.gov.hmcts.reform.preapi.dto.RegionDTO; import uk.gov.hmcts.reform.preapi.entities.ShareBooking; - -import java.sql.Timestamp; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; -import java.util.stream.Stream; - +import uk.gov.hmcts.reform.preapi.utils.DateTimeUtils; @Data @NoArgsConstructor +@EqualsAndHashCode(callSuper = true) @Schema(description = "SharedReportDTO") @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -public class SharedReportDTO { - - @Schema(description = "SharedReportSharedAt") - private Timestamp sharedAt; +public class SharedReportDTO extends BaseReportDTO { - @Schema(description = "SharedReportAllocatedTo") - private String allocatedTo; + @Schema(description = "SharedReportShareDate") + private String shareDate; - @Schema(description = "SharedReportAllocatedToFullName") - private String allocatedToFullName; + @Schema(description = "SharedReportShareTime") + private String shareTime; - @Schema(description = "SharedReportAllocatedBy") - private String allocatedBy; + @Schema(description = "SharedReportShareTimezone") + private String timezone; - @Schema(description = "SharedReportAllocatedToFullName") - private String allocatedByFullName; + @Schema(description = "SharedReportSharedWith") + private String sharedWith; - @Schema(description = "SharedReportCaseReference") - private String caseReference; + @Schema(description = "SharedReportSharedWithFullName") + private String sharedWithFullName; - @Schema(description = "SharedReportCourtName") - private String court; + @Schema(description = "SharedReportOrganisationAllocatedTo") + private String organisationSharedWith; - @Schema(description = "SharedReportRegions") - private Set regions; + @Schema(description = "SharedReportGrantedBy") + private String grantedBy; - @Schema(description = "SharedReportBookingId") - private UUID bookingId; + @Schema(description = "SharedReportGrantedByFullName") + private String grantedByFullName; public SharedReportDTO(ShareBooking shareBooking) { - sharedAt = shareBooking.getCreatedAt(); - allocatedTo = shareBooking.getSharedWith().getEmail(); - allocatedToFullName = shareBooking.getSharedWith().getFullName(); - allocatedBy = shareBooking.getSharedBy().getEmail(); - allocatedByFullName = shareBooking.getSharedBy().getFullName(); - var caseEntity = shareBooking.getBooking().getCaseId(); - caseReference = caseEntity.getReference(); - court = caseEntity.getCourt().getName(); - regions = Stream.ofNullable(caseEntity.getCourt().getRegions()) - .flatMap(regions -> regions.stream().map(RegionDTO::new)) - .collect(Collectors.toSet()); - bookingId = shareBooking.getBooking().getId(); + super(shareBooking.getBooking().getCaseId()); + shareDate = DateTimeUtils.formatDate(shareBooking.getCreatedAt()); + shareTime = DateTimeUtils.formatTime(shareBooking.getCreatedAt()); + timezone = DateTimeUtils.getTimezoneAbbreviation(shareBooking.getCreatedAt()); + + sharedWith = shareBooking.getSharedWith().getEmail(); + sharedWithFullName = shareBooking.getSharedWith().getFullName(); + organisationSharedWith = shareBooking.getSharedWith().getOrganisation(); + + grantedBy = shareBooking.getSharedBy().getEmail(); + grantedByFullName = shareBooking.getSharedBy().getFullName(); } } diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/entities/Court.java b/src/main/java/uk/gov/hmcts/reform/preapi/entities/Court.java index ab22fef2c..8d16871bc 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/entities/Court.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/entities/Court.java @@ -20,7 +20,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; - @Getter @Setter @Entity @@ -37,6 +36,12 @@ public class Court extends BaseEntity { @Column(name = "location_code", length = 25) private String locationCode; + @Column(name = "county") + private String county; + + @Column(name = "postcode", length = 8) + private String postcode; + @ManyToMany @JoinTable( name = "court_region", @@ -59,6 +64,8 @@ public HashMap getDetailsForAudit() { details.put("courtName", name); details.put("courtType", courtType); details.put("courtLocationCode", locationCode); + details.put("courtCounty", county); + details.put("courtPostcode", postcode); details.put("courtRegions", Stream.ofNullable(getRegions()) .flatMap(regions -> regions.stream().map(Region::getName)) .collect(Collectors.toSet())); diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/entities/Participant.java b/src/main/java/uk/gov/hmcts/reform/preapi/entities/Participant.java index 48613dae8..3434dfb68 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/entities/Participant.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/entities/Participant.java @@ -9,6 +9,7 @@ import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; +import jakarta.persistence.Transient; import lombok.Getter; import lombok.Setter; import org.hibernate.annotations.JdbcTypeCode; @@ -46,6 +47,13 @@ public class Participant extends CreatedModifiedAtEntity { @ManyToMany(mappedBy = "participants") private Set bookings; + @Transient + private String fullName; + + public String getFullName() { + return firstName + " " + lastName; + } + @Override public HashMap getDetailsForAudit() { var details = new HashMap(); diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/repositories/ShareBookingRepository.java b/src/main/java/uk/gov/hmcts/reform/preapi/repositories/ShareBookingRepository.java index 9b9dae5b4..5d3bf94f7 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/repositories/ShareBookingRepository.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/repositories/ShareBookingRepository.java @@ -38,13 +38,17 @@ public interface ShareBookingRepository extends JpaRepository searchAll( @Param("courtId") UUID courtId, @Param("bookingId") UUID bookingId, @Param("sharedWithId") UUID sharedWithId, - @Param("sharedWithEmail") String sharedWithEmail + @Param("sharedWithEmail") String sharedWithEmail, + @Param("onlyActive") boolean onlyActive ); List findAllByBookingAndDeletedAtIsNull(Booking booking); diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/services/CourtService.java b/src/main/java/uk/gov/hmcts/reform/preapi/services/CourtService.java index 3e7854b87..5dfa319aa 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/services/CourtService.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/services/CourtService.java @@ -91,6 +91,8 @@ public UpsertResult upsert(CreateCourtDTO createCourtDTO) { courtEntity.setName(createCourtDTO.getName()); courtEntity.setCourtType(createCourtDTO.getCourtType()); courtEntity.setLocationCode(createCourtDTO.getLocationCode()); + courtEntity.setCounty(createCourtDTO.getCounty()); + courtEntity.setPostcode(createCourtDTO.getPostcode()); courtEntity.setRegions(regions); courtEntity.setRooms(rooms); diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/services/LegacyReportService.java b/src/main/java/uk/gov/hmcts/reform/preapi/services/LegacyReportService.java new file mode 100644 index 000000000..555ae8a3b --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/preapi/services/LegacyReportService.java @@ -0,0 +1,194 @@ +package uk.gov.hmcts.reform.preapi.services; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.AccessRemovedReportDTO; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.CompletedCaptureSessionReportDTO; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.ConcurrentCaptureSessionReportDTO; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.EditReportDTO; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.PlaybackReportDTO; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.RecordingsPerCaseReportDTO; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.ScheduleReportDTO; +import uk.gov.hmcts.reform.preapi.dto.legacyreports.SharedReportDTO; +import uk.gov.hmcts.reform.preapi.entities.AppAccess; +import uk.gov.hmcts.reform.preapi.entities.Audit; +import uk.gov.hmcts.reform.preapi.entities.Case; +import uk.gov.hmcts.reform.preapi.entities.PortalAccess; +import uk.gov.hmcts.reform.preapi.entities.Recording; +import uk.gov.hmcts.reform.preapi.entities.ShareBooking; +import uk.gov.hmcts.reform.preapi.enums.AuditLogSource; +import uk.gov.hmcts.reform.preapi.enums.RecordingStatus; +import uk.gov.hmcts.reform.preapi.exception.NotFoundException; +import uk.gov.hmcts.reform.preapi.repositories.AppAccessRepository; +import uk.gov.hmcts.reform.preapi.repositories.AuditRepository; +import uk.gov.hmcts.reform.preapi.repositories.CaptureSessionRepository; +import uk.gov.hmcts.reform.preapi.repositories.PortalAccessRepository; +import uk.gov.hmcts.reform.preapi.repositories.RecordingRepository; +import uk.gov.hmcts.reform.preapi.repositories.ShareBookingRepository; +import uk.gov.hmcts.reform.preapi.repositories.UserRepository; + +import java.util.Comparator; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +@Service +public class LegacyReportService { + + private final CaptureSessionRepository captureSessionRepository; + private final RecordingRepository recordingRepository; + private final ShareBookingRepository shareBookingRepository; + private final AuditRepository auditRepository; + private final UserRepository userRepository; + private final AppAccessRepository appAccessRepository; + private final PortalAccessRepository portalAccessRepository; + + @Autowired + public LegacyReportService(CaptureSessionRepository captureSessionRepository, + RecordingRepository recordingRepository, + ShareBookingRepository shareBookingRepository, + AuditRepository auditRepository, + UserRepository userRepository, + AppAccessRepository appAccessRepository, + PortalAccessRepository portalAccessRepository) { + this.captureSessionRepository = captureSessionRepository; + this.recordingRepository = recordingRepository; + this.shareBookingRepository = shareBookingRepository; + this.auditRepository = auditRepository; + this.userRepository = userRepository; + this.appAccessRepository = appAccessRepository; + this.portalAccessRepository = portalAccessRepository; + } + + @Transactional + public List reportCaptureSessions() { + return captureSessionRepository + .reportConcurrentCaptureSessions() + .stream() + .map(ConcurrentCaptureSessionReportDTO::new) + .collect(Collectors.toList()); + } + + @Transactional + public List reportRecordingsPerCase() { + return recordingRepository + .countRecordingsPerCase() + .stream() + .map(data -> new RecordingsPerCaseReportDTO((Case) data[0], ((Long) data[1]).intValue())) + .toList(); + } + + @Transactional + public List reportEdits() { + return recordingRepository + .findAllByParentRecordingIsNotNull() + .stream() + .sorted(Comparator.comparing(Recording::getCreatedAt)) + .map(EditReportDTO::new) + .collect(Collectors.toList()); + } + + @Transactional + public List reportShared( + UUID courtId, + UUID bookingId, + UUID sharedWithId, + String sharedWithEmail + ) { + return shareBookingRepository + .searchAll(courtId, bookingId, sharedWithId, sharedWithEmail, false) + .stream() + .sorted(Comparator.comparing(ShareBooking::getCreatedAt)) + .map(SharedReportDTO::new) + .collect(Collectors.toList()); + } + + @Transactional + public List reportScheduled() { + return captureSessionRepository + .findAllByStatus(RecordingStatus.RECORDING_AVAILABLE) + .stream() + .sorted(Comparator.comparing(c -> c.getBooking().getScheduledFor())) + .map(ScheduleReportDTO::new) + .collect(Collectors.toList()); + } + + @Transactional + public List reportPlayback(AuditLogSource source) { + if (source == null) { + return auditRepository + .findAllAccessAttempts() + .stream() + .map(this::toPlaybackReport) + .toList(); + } else if (source == AuditLogSource.PORTAL || source == AuditLogSource.APPLICATION) { + final var activityPlay = "Play"; + final var functionalAreaVideoPlayer = "Video Player"; + final var functionalAreaViewRecordings = "View Recordings"; + + return auditRepository + .findBySourceAndFunctionalAreaAndActivity( + source, + source == AuditLogSource.PORTAL + ? functionalAreaVideoPlayer + : functionalAreaViewRecordings, + activityPlay + ) + .stream() + .map(this::toPlaybackReport) + .toList(); + } else { + throw new NotFoundException("Report for playback source: " + source); + } + } + + @Transactional + public List reportCompletedCaptureSessions() { + return recordingRepository + .findAllByParentRecordingIsNull() + .stream() + .sorted(Comparator.comparing(r -> r.getCaptureSession().getBooking().getScheduledFor())) + .map(CompletedCaptureSessionReportDTO::new) + .collect(Collectors.toList()); + } + + @Transactional + public List reportAccessRemoved() { + return shareBookingRepository + .findAllByDeletedAtIsNotNull() + .stream() + .sorted(Comparator.comparing(ShareBooking::getDeletedAt)) + .map(AccessRemovedReportDTO::new) + .collect(Collectors.toList()); + } + + private PlaybackReportDTO toPlaybackReport(Audit audit) { + // S28-3604 discovered audit details records Recording Id as recordingId _and_ recordinguid + var auditDetails = audit.getAuditDetails() != null && !audit.getAuditDetails().isNull(); + UUID recordingId = null; + if (auditDetails) { + if (audit.getAuditDetails().hasNonNull("recordingId")) { + recordingId = UUID.fromString(audit.getAuditDetails().get("recordingId").asText()); + } else if (audit.getAuditDetails().hasNonNull("recordinguid")) { + recordingId = UUID.fromString(audit.getAuditDetails().get("recordinguid").asText()); + } + } + + return new PlaybackReportDTO( + audit, + audit.getCreatedBy() != null + ? userRepository + .findById(audit.getCreatedBy()) + .orElse(appAccessRepository.findById(audit.getCreatedBy()) + .map(AppAccess::getUser) + .orElse(portalAccessRepository.findById(audit.getCreatedBy()) + .map(PortalAccess::getUser) + .orElse(null))) + : null, + recordingId != null + ? recordingRepository.findById(recordingId).orElse(null) + : null + ); + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/services/ReportService.java b/src/main/java/uk/gov/hmcts/reform/preapi/services/ReportService.java index d2007f3c0..7f0ed053d 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/services/ReportService.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/services/ReportService.java @@ -18,6 +18,7 @@ import uk.gov.hmcts.reform.preapi.entities.Case; import uk.gov.hmcts.reform.preapi.entities.PortalAccess; import uk.gov.hmcts.reform.preapi.entities.Recording; +import uk.gov.hmcts.reform.preapi.entities.ShareBooking; import uk.gov.hmcts.reform.preapi.enums.AuditLogSource; import uk.gov.hmcts.reform.preapi.enums.RecordingStatus; import uk.gov.hmcts.reform.preapi.exception.NotFoundException; @@ -85,9 +86,9 @@ public List reportEdits() { return recordingRepository .findAllByParentRecordingIsNotNull() .stream() + .sorted(Comparator.comparing(Recording::getCreatedAt)) .map(EditReportDTO::new) - .sorted(Comparator.comparing(EditReportDTO::getCreatedAt)) - .toList(); + .collect(Collectors.toList()); } @Transactional @@ -95,14 +96,15 @@ public List reportShared( UUID courtId, UUID bookingId, UUID sharedWithId, - String sharedWithEmail + String sharedWithEmail, + boolean onlyActive ) { return shareBookingRepository - .searchAll(courtId, bookingId, sharedWithId, sharedWithEmail) + .searchAll(courtId, bookingId, sharedWithId, sharedWithEmail, onlyActive) .stream() + .sorted(Comparator.comparing(ShareBooking::getCreatedAt)) .map(SharedReportDTO::new) - .sorted(Comparator.comparing(SharedReportDTO::getSharedAt)) - .toList(); + .collect(Collectors.toList()); } @Transactional @@ -110,9 +112,9 @@ public List reportScheduled() { return captureSessionRepository .findAllByStatus(RecordingStatus.RECORDING_AVAILABLE) .stream() + .sorted(Comparator.comparing(c -> c.getBooking().getScheduledFor())) .map(ScheduleReportDTO::new) - .sorted(Comparator.comparing(ScheduleReportDTO::getScheduledFor)) - .toList(); + .collect(Collectors.toList()); } @Transactional @@ -149,9 +151,9 @@ public List reportCompletedCaptureSessions() { return recordingRepository .findAllByParentRecordingIsNull() .stream() + .sorted(Comparator.comparing(r -> r.getCaptureSession().getBooking().getScheduledFor())) .map(CompletedCaptureSessionReportDTO::new) - .sorted(Comparator.comparing(CompletedCaptureSessionReportDTO::getScheduledFor)) - .toList(); + .collect(Collectors.toList()); } @Transactional @@ -159,9 +161,9 @@ public List reportAccessRemoved() { return shareBookingRepository .findAllByDeletedAtIsNotNull() .stream() + .sorted(Comparator.comparing(ShareBooking::getDeletedAt)) .map(AccessRemovedReportDTO::new) - .sorted(Comparator.comparing(AccessRemovedReportDTO::getRemovedAt)) - .toList(); + .collect(Collectors.toList()); } @Transactional diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/utils/DateTimeUtils.java b/src/main/java/uk/gov/hmcts/reform/preapi/utils/DateTimeUtils.java new file mode 100644 index 000000000..1798bc14b --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/preapi/utils/DateTimeUtils.java @@ -0,0 +1,46 @@ +package uk.gov.hmcts.reform.preapi.utils; + +import lombok.experimental.UtilityClass; + +import java.sql.Timestamp; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.util.Locale; + +@UtilityClass +public class DateTimeUtils { + public static final ZoneId TIME_ZONE = ZoneId.of("Europe/London"); + + // Date Format DD/MM/YY + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT) + .withLocale(Locale.UK); + // Time Format HH:MM:SS + private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM) + .withLocale(Locale.UK); + + public String formatDate(Timestamp timestamp) { + if (timestamp == null) { + throw new IllegalArgumentException("Timestamp cannot be null"); + } + return timestamp.toInstant().atZone(TIME_ZONE).format(DATE_FORMATTER); + } + + public String formatTime(Timestamp timestamp) { + if (timestamp == null) { + throw new IllegalArgumentException("Timestamp cannot be null"); + } + return timestamp.toInstant().atZone(TIME_ZONE).format(TIME_FORMATTER); + } + + public boolean isDaylightSavings(Timestamp timestamp) { + if (timestamp == null) { + throw new IllegalArgumentException("Timestamp cannot be null"); + } + return TIME_ZONE.getRules().isDaylightSavings(timestamp.toInstant()); + } + + public String getTimezoneAbbreviation(Timestamp timestamp) { + return isDaylightSavings(timestamp) ? "BST" : "GMT"; + } +} diff --git a/src/main/resources/db/migration/V032__CourtPostcodeAndCounty.sql b/src/main/resources/db/migration/V032__CourtPostcodeAndCounty.sql new file mode 100644 index 000000000..d2e9f7e8d --- /dev/null +++ b/src/main/resources/db/migration/V032__CourtPostcodeAndCounty.sql @@ -0,0 +1,2 @@ +ALTER TABLE public.courts ADD COLUMN county VARCHAR(255); +ALTER TABLE public.courts ADD COLUMN postcode VARCHAR(8); diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/controller/ReportControllerTest.java b/src/test/java/uk/gov/hmcts/reform/preapi/controller/ReportControllerTest.java index e75b6fef5..911a8f9ed 100644 --- a/src/test/java/uk/gov/hmcts/reform/preapi/controller/ReportControllerTest.java +++ b/src/test/java/uk/gov/hmcts/reform/preapi/controller/ReportControllerTest.java @@ -1,5 +1,6 @@ package uk.gov.hmcts.reform.preapi.controller; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -14,17 +15,24 @@ import uk.gov.hmcts.reform.preapi.dto.reports.ConcurrentCaptureSessionReportDTO; import uk.gov.hmcts.reform.preapi.dto.reports.EditReportDTO; import uk.gov.hmcts.reform.preapi.dto.reports.PlaybackReportDTO; -import uk.gov.hmcts.reform.preapi.dto.reports.RecordingParticipantsReportDTO; import uk.gov.hmcts.reform.preapi.dto.reports.RecordingsPerCaseReportDTO; import uk.gov.hmcts.reform.preapi.dto.reports.ScheduleReportDTO; import uk.gov.hmcts.reform.preapi.dto.reports.SharedReportDTO; import uk.gov.hmcts.reform.preapi.dto.reports.UserPrimaryCourtReportDTO; +import uk.gov.hmcts.reform.preapi.entities.Audit; +import uk.gov.hmcts.reform.preapi.entities.Booking; +import uk.gov.hmcts.reform.preapi.entities.CaptureSession; +import uk.gov.hmcts.reform.preapi.entities.Case; +import uk.gov.hmcts.reform.preapi.entities.Court; +import uk.gov.hmcts.reform.preapi.entities.Recording; +import uk.gov.hmcts.reform.preapi.entities.Region; +import uk.gov.hmcts.reform.preapi.entities.User; import uk.gov.hmcts.reform.preapi.enums.AuditLogSource; -import uk.gov.hmcts.reform.preapi.enums.ParticipantType; import uk.gov.hmcts.reform.preapi.enums.RecordingStatus; import uk.gov.hmcts.reform.preapi.security.service.UserAuthenticationService; import uk.gov.hmcts.reform.preapi.services.ReportService; import uk.gov.hmcts.reform.preapi.services.ScheduledTaskRunner; +import uk.gov.hmcts.reform.preapi.utils.DateTimeUtils; import java.sql.Timestamp; import java.time.Duration; @@ -33,6 +41,7 @@ import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Set; +import java.util.TimeZone; import java.util.UUID; import static org.mockito.Mockito.times; @@ -58,24 +67,33 @@ public class ReportControllerTest { @MockitoBean private ScheduledTaskRunner taskRunner; + @BeforeAll + static void setUp() { + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + } + @DisplayName("Should get a report containing a list of concurrent capture sessions") @Test void reportConcurrentCaptureSessionsSuccess() throws Exception { var reportItem = new ConcurrentCaptureSessionReportDTO(); - reportItem.setId(UUID.randomUUID()); - reportItem.setStartTime(Timestamp.from(Instant.now())); - reportItem.setEndTime(Timestamp.from(Instant.now())); + var timestamp = Timestamp.from(Instant.now()); + var timestampPlus1 = Timestamp.from(Instant.now().plusSeconds(3600)); + reportItem.setDate(DateTimeUtils.formatDate(timestamp)); + reportItem.setStartTime(DateTimeUtils.formatTime(timestamp)); + reportItem.setEndTime(DateTimeUtils.formatTime(timestampPlus1)); reportItem.setDuration(Duration.ofMinutes(3)); reportItem.setCourt("Example Court"); reportItem.setCaseReference("ABC123"); when(reportService.reportCaptureSessions()).thenReturn(List.of(reportItem)); - mockMvc.perform(get("/reports/capture-sessions-concurrent")) + mockMvc.perform(get("/reports-v2/capture-sessions-concurrent")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$[0].id").value(reportItem.getId().toString())) - .andExpect(jsonPath("$[0].duration").value("PT3M")); + .andExpect(jsonPath("$[0].date").value(reportItem.getDate())) + .andExpect(jsonPath("$[0].start_time").value(reportItem.getStartTime())) + .andExpect(jsonPath("$[0].end_time").value(reportItem.getEndTime())) + .andExpect(jsonPath("$[0].duration").value("00:03:00")); } @DisplayName("Should get a report containing a list of cases with the count of completed capture sessions") @@ -84,279 +102,319 @@ void reportRecordingsPerCaseSuccess() throws Exception { var reportItem = new RecordingsPerCaseReportDTO(); reportItem.setCaseReference("ABC123"); reportItem.setCourt("Example Court"); - reportItem.setRegions(Set.of()); - reportItem.setCount(2); + reportItem.setCounty("Example County"); + reportItem.setPostcode("AB1 2CD"); + reportItem.setRegion("Example Region"); + reportItem.setNumberOfRecordings(2); when(reportService.reportRecordingsPerCase()).thenReturn(List.of(reportItem)); - mockMvc.perform(get("/reports/recordings-per-case")) + mockMvc.perform(get("/reports-v2/recordings-per-case")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$[0].case_reference").value(reportItem.getCaseReference())) - .andExpect(jsonPath("$[0].count").value(reportItem.getCount())); + .andExpect(jsonPath("$[0].court").value(reportItem.getCourt())) + .andExpect(jsonPath("$[0].county").value(reportItem.getCounty())) + .andExpect(jsonPath("$[0].postcode").value(reportItem.getPostcode())) + .andExpect(jsonPath("$[0].region").value(reportItem.getRegion())) + .andExpect(jsonPath("$[0].number_of_recordings").value(reportItem.getNumberOfRecordings())); } @DisplayName("Should get a report containing a list of edited recordings") @Test void reportEditsSuccess() throws Exception { var reportItem = new EditReportDTO(); - reportItem.setCreatedAt(Timestamp.from(Instant.now())); + var timestamp = Timestamp.from(Instant.now()); + reportItem.setEditDate(DateTimeUtils.formatDate(timestamp)); + reportItem.setEditTime(DateTimeUtils.formatTime(timestamp)); + reportItem.setTimezone(DateTimeUtils.getTimezoneAbbreviation(timestamp)); reportItem.setVersion(2); reportItem.setCaseReference("ABC123"); reportItem.setCourt("Example Court"); - reportItem.setRegions(Set.of()); - reportItem.setRecordingId(UUID.randomUUID()); + reportItem.setCounty("Example County"); + reportItem.setPostcode("AB1 2CD"); + reportItem.setRegion("Somewhere"); when(reportService.reportEdits()).thenReturn(List.of(reportItem)); - mockMvc.perform(get("/reports/edits")) + mockMvc.perform(get("/reports-v2/edits")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$[0].case_reference").value(reportItem.getCaseReference())) + .andExpect(jsonPath("$[0].edit_date").value(reportItem.getEditDate())) + .andExpect(jsonPath("$[0].edit_time").value(reportItem.getEditTime())) + .andExpect(jsonPath("$[0].timezone").value(reportItem.getTimezone())) .andExpect(jsonPath("$[0].version").value(reportItem.getVersion())) - .andExpect(jsonPath("$[0].recording_id").value(reportItem.getRecordingId().toString())); + .andExpect(jsonPath("$[0].case_reference").value(reportItem.getCaseReference())) + .andExpect(jsonPath("$[0].court").value(reportItem.getCourt())) + .andExpect(jsonPath("$[0].county").value(reportItem.getCounty())) + .andExpect(jsonPath("$[0].postcode").value(reportItem.getPostcode())) + .andExpect(jsonPath("$[0].region").value(reportItem.getRegion())); } @DisplayName("Should get a report containing a list of details relating to shared bookings") @Test void reportBookingsSharedSuccess() throws Exception { - var reportItem = new SharedReportDTO(); - reportItem.setSharedAt(Timestamp.from(Instant.now())); - reportItem.setAllocatedTo("example1@example.com"); - reportItem.setAllocatedToFullName("Example One"); - reportItem.setAllocatedBy("example2@example.com"); - reportItem.setAllocatedByFullName("Example Two"); - reportItem.setCaseReference("ABC123"); - reportItem.setCourt("Example Court"); - reportItem.setRegions(Set.of()); - reportItem.setBookingId(UUID.randomUUID()); + var reportItem = createSharedReport(); - when(reportService.reportShared(null, null, null, null)).thenReturn(List.of(reportItem)); - mockMvc.perform(get("/reports/shared-bookings")) + when(reportService.reportShared(null, null, null, null, false)).thenReturn(List.of(reportItem)); + mockMvc.perform(get("/reports-v2/shared-bookings")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$[0].allocated_to").value(reportItem.getAllocatedTo())) - .andExpect(jsonPath("$[0].allocated_to_full_name").value(reportItem.getAllocatedToFullName())) - .andExpect(jsonPath("$[0].allocated_by").value(reportItem.getAllocatedBy())) - .andExpect(jsonPath("$[0].allocated_by_full_name").value(reportItem.getAllocatedByFullName())) + .andExpect(jsonPath("$[0].share_date").value(reportItem.getShareDate())) + .andExpect(jsonPath("$[0].share_time").value(reportItem.getShareTime())) + .andExpect(jsonPath("$[0].timezone").value(reportItem.getTimezone())) + .andExpect(jsonPath("$[0].shared_with").value(reportItem.getSharedWith())) + .andExpect(jsonPath("$[0].shared_with_full_name").value(reportItem.getSharedWithFullName())) + .andExpect(jsonPath("$[0].granted_by").value(reportItem.getGrantedBy())) + .andExpect(jsonPath("$[0].granted_by_full_name").value(reportItem.getGrantedByFullName())) .andExpect(jsonPath("$[0].case_reference").value(reportItem.getCaseReference())) .andExpect(jsonPath("$[0].court").value(reportItem.getCourt())) - .andExpect(jsonPath("$[0].booking_id").value(reportItem.getBookingId().toString())); + .andExpect(jsonPath("$[0].county").value(reportItem.getCounty())) + .andExpect(jsonPath("$[0].postcode").value(reportItem.getPostcode())) + .andExpect(jsonPath("$[0].region").value(reportItem.getRegion())); - verify(reportService, times(1)).reportShared(null, null, null, null); + verify(reportService, times(1)).reportShared(null, null, null, null, false); } @DisplayName("Should get a report containing a list of details relating to shared bookings filtered by court") @Test void reportBookingsSharedFilterCourtSuccess() throws Exception { - var reportItem = new SharedReportDTO(); - reportItem.setSharedAt(Timestamp.from(Instant.now())); - reportItem.setAllocatedTo("example1@example.com"); - reportItem.setAllocatedToFullName("Example One"); - reportItem.setAllocatedBy("example2@example.com"); - reportItem.setAllocatedByFullName("Example Two"); - reportItem.setCaseReference("ABC123"); - reportItem.setCourt("Example Court"); - reportItem.setRegions(Set.of()); - reportItem.setBookingId(UUID.randomUUID()); - + var reportItem = createSharedReport(); var courtId = UUID.randomUUID(); - when(reportService.reportShared(courtId, null, null, null)).thenReturn(List.of(reportItem)); - mockMvc.perform(get("/reports/shared-bookings") + when(reportService.reportShared(courtId, null, null, null, false)).thenReturn(List.of(reportItem)); + mockMvc.perform(get("/reports-v2/shared-bookings") .param("courtId", courtId.toString())) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$[0].allocated_to").value(reportItem.getAllocatedTo())) - .andExpect(jsonPath("$[0].allocated_to_full_name").value(reportItem.getAllocatedToFullName())) - .andExpect(jsonPath("$[0].allocated_by").value(reportItem.getAllocatedBy())) - .andExpect(jsonPath("$[0].allocated_by_full_name").value(reportItem.getAllocatedByFullName())) - .andExpect(jsonPath("$[0].case_reference").value(reportItem.getCaseReference())) - .andExpect(jsonPath("$[0].court").value(reportItem.getCourt())) - .andExpect(jsonPath("$[0].booking_id").value(reportItem.getBookingId().toString())); - - verify(reportService, times(1)).reportShared(courtId, null, null, null); + .andExpect(jsonPath("$[0].share_date").value(reportItem.getShareDate())) + .andExpect(jsonPath("$[0].share_time").value(reportItem.getShareTime())) + .andExpect(jsonPath("$[0].timezone").value(reportItem.getTimezone())) + .andExpect(jsonPath("$[0].shared_with").value(reportItem.getSharedWith())) + .andExpect(jsonPath("$[0].shared_with_full_name").value(reportItem.getSharedWithFullName())) + .andExpect(jsonPath("$[0].granted_by").value(reportItem.getGrantedBy())) + .andExpect(jsonPath("$[0].granted_by_full_name").value(reportItem.getGrantedByFullName())); + + verify(reportService, times(1)).reportShared(courtId, null, null, null, false); } @DisplayName("Should get a report containing a list of details relating to shared bookings filtered by booking") @Test void reportBookingsSharedFilterBookingSuccess() throws Exception { - var reportItem = new SharedReportDTO(); - reportItem.setSharedAt(Timestamp.from(Instant.now())); - reportItem.setAllocatedTo("example1@example.com"); - reportItem.setAllocatedToFullName("Example One"); - reportItem.setAllocatedBy("example2@example.com"); - reportItem.setAllocatedByFullName("Example Two"); - reportItem.setCaseReference("ABC123"); - reportItem.setCourt("Example Court"); - reportItem.setRegions(Set.of()); - reportItem.setBookingId(UUID.randomUUID()); + var reportItem = createSharedReport(); + var searchId = UUID.randomUUID(); - when(reportService.reportShared(null, reportItem.getBookingId(), null, null)).thenReturn(List.of(reportItem)); - mockMvc.perform(get("/reports/shared-bookings") - .param("bookingId", reportItem.getBookingId().toString())) + when(reportService.reportShared(null, searchId, null, null, false)) + .thenReturn(List.of(reportItem)); + + mockMvc.perform(get("/reports-v2/shared-bookings") + .param("bookingId", searchId.toString())) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$[0].allocated_to").value(reportItem.getAllocatedTo())) - .andExpect(jsonPath("$[0].allocated_to_full_name").value(reportItem.getAllocatedToFullName())) - .andExpect(jsonPath("$[0].allocated_by").value(reportItem.getAllocatedBy())) - .andExpect(jsonPath("$[0].allocated_by_full_name").value(reportItem.getAllocatedByFullName())) - .andExpect(jsonPath("$[0].case_reference").value(reportItem.getCaseReference())) - .andExpect(jsonPath("$[0].court").value(reportItem.getCourt())) - .andExpect(jsonPath("$[0].booking_id").value(reportItem.getBookingId().toString())); - - verify(reportService, times(1)).reportShared(null, reportItem.getBookingId(), null, null); + .andExpect(jsonPath("$[0].share_date").value(reportItem.getShareDate())) + .andExpect(jsonPath("$[0].share_time").value(reportItem.getShareTime())) + .andExpect(jsonPath("$[0].timezone").value(reportItem.getTimezone())) + .andExpect(jsonPath("$[0].shared_with").value(reportItem.getSharedWith())) + .andExpect(jsonPath("$[0].shared_with_full_name").value(reportItem.getSharedWithFullName())) + .andExpect(jsonPath("$[0].granted_by").value(reportItem.getGrantedBy())) + .andExpect(jsonPath("$[0].granted_by_full_name").value(reportItem.getGrantedByFullName())); + + verify(reportService, times(1)).reportShared(null, searchId, null, null, false); } @DisplayName("Should get a report containing a list of details relating to shared bookings filtered by user id") @Test void reportBookingsSharedFilterUserIdSuccess() throws Exception { - var reportItem = new SharedReportDTO(); - reportItem.setSharedAt(Timestamp.from(Instant.now())); - reportItem.setAllocatedTo("example1@example.com"); - reportItem.setAllocatedToFullName("Example One"); - reportItem.setAllocatedBy("example2@example.com"); - reportItem.setAllocatedByFullName("Example Two"); - reportItem.setCaseReference("ABC123"); - reportItem.setCourt("Example Court"); - reportItem.setRegions(Set.of()); - reportItem.setBookingId(UUID.randomUUID()); + var reportItem = createSharedReport(); var userId = UUID.randomUUID(); - when(reportService.reportShared(null, null, userId, null)).thenReturn(List.of(reportItem)); - mockMvc.perform(get("/reports/shared-bookings") + when(reportService.reportShared(null, null, userId, null, false)).thenReturn(List.of(reportItem)); + mockMvc.perform(get("/reports-v2/shared-bookings") .param("sharedWithId", userId.toString())) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$[0].allocated_to").value(reportItem.getAllocatedTo())) - .andExpect(jsonPath("$[0].allocated_to_full_name").value(reportItem.getAllocatedToFullName())) - .andExpect(jsonPath("$[0].allocated_by").value(reportItem.getAllocatedBy())) - .andExpect(jsonPath("$[0].allocated_by_full_name").value(reportItem.getAllocatedByFullName())) - .andExpect(jsonPath("$[0].case_reference").value(reportItem.getCaseReference())) - .andExpect(jsonPath("$[0].court").value(reportItem.getCourt())) - .andExpect(jsonPath("$[0].booking_id").value(reportItem.getBookingId().toString())); - - verify(reportService, times(1)).reportShared(null, null, userId, null); + .andExpect(jsonPath("$[0].share_date").value(reportItem.getShareDate())) + .andExpect(jsonPath("$[0].share_time").value(reportItem.getShareTime())) + .andExpect(jsonPath("$[0].timezone").value(reportItem.getTimezone())) + .andExpect(jsonPath("$[0].shared_with").value(reportItem.getSharedWith())) + .andExpect(jsonPath("$[0].shared_with_full_name").value(reportItem.getSharedWithFullName())) + .andExpect(jsonPath("$[0].granted_by").value(reportItem.getGrantedBy())) + .andExpect(jsonPath("$[0].granted_by_full_name").value(reportItem.getGrantedByFullName())); + + verify(reportService, times(1)).reportShared(null, null, userId, null, false); } @DisplayName("Should get a report containing a list of details relating to shared bookings filtered by user email") @Test void reportBookingsSharedFilterUserEmailSuccess() throws Exception { - var reportItem = new SharedReportDTO(); - reportItem.setSharedAt(Timestamp.from(Instant.now())); - reportItem.setAllocatedTo("example1@example.com"); - reportItem.setAllocatedToFullName("Example One"); - reportItem.setAllocatedBy("example2@example.com"); - reportItem.setAllocatedByFullName("Example Two"); - reportItem.setCaseReference("ABC123"); - reportItem.setCourt("Example Court"); - reportItem.setRegions(Set.of()); - reportItem.setBookingId(UUID.randomUUID()); + var reportItem = createSharedReport(); - when(reportService.reportShared(null, null, null, reportItem.getAllocatedTo())).thenReturn(List.of(reportItem)); - mockMvc.perform(get("/reports/shared-bookings") - .param("sharedWithEmail", reportItem.getAllocatedTo())) + when(reportService.reportShared(null, null, null, reportItem.getSharedWith(), false)) + .thenReturn(List.of(reportItem)); + + mockMvc.perform(get("/reports-v2/shared-bookings") + .param("sharedWithEmail", reportItem.getSharedWith())) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$[0].allocated_to").value(reportItem.getAllocatedTo())) - .andExpect(jsonPath("$[0].allocated_to_full_name").value(reportItem.getAllocatedToFullName())) - .andExpect(jsonPath("$[0].allocated_by").value(reportItem.getAllocatedBy())) - .andExpect(jsonPath("$[0].allocated_by_full_name").value(reportItem.getAllocatedByFullName())) - .andExpect(jsonPath("$[0].case_reference").value(reportItem.getCaseReference())) - .andExpect(jsonPath("$[0].court").value(reportItem.getCourt())) - .andExpect(jsonPath("$[0].booking_id").value(reportItem.getBookingId().toString())); + .andExpect(jsonPath("$[0].share_date").value(reportItem.getShareDate())) + .andExpect(jsonPath("$[0].share_time").value(reportItem.getShareTime())) + .andExpect(jsonPath("$[0].timezone").value(reportItem.getTimezone())) + .andExpect(jsonPath("$[0].shared_with").value(reportItem.getSharedWith())) + .andExpect(jsonPath("$[0].shared_with_full_name").value(reportItem.getSharedWithFullName())) + .andExpect(jsonPath("$[0].granted_by").value(reportItem.getGrantedBy())) + .andExpect(jsonPath("$[0].granted_by_full_name").value(reportItem.getGrantedByFullName())); + - verify(reportService, times(1)).reportShared(null, null, null, reportItem.getAllocatedTo()); + verify(reportService, times(1)).reportShared(null, null, null, reportItem.getSharedWith(), false); + } + + @Test + @DisplayName("Should get a report containing a list detailing to shared bookings filtered by only active shares") + void reportBookingsSharedFilterOnlyActiveSuccess() throws Exception { + var reportItem = createSharedReport(); + + when(reportService.reportShared(null, null, null, null, true)) + .thenReturn(List.of(reportItem)); + mockMvc.perform(get("/reports-v2/shared-bookings") + .param("onlyActive", "true")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$[0].share_date").value(reportItem.getShareDate())) + .andExpect(jsonPath("$[0].share_time").value(reportItem.getShareTime())) + .andExpect(jsonPath("$[0].timezone").value(reportItem.getTimezone())) + .andExpect(jsonPath("$[0].shared_with").value(reportItem.getSharedWith())) + .andExpect(jsonPath("$[0].shared_with_full_name").value(reportItem.getSharedWithFullName())) + .andExpect(jsonPath("$[0].organisation_shared_with").value(reportItem.getOrganisationSharedWith())) + .andExpect(jsonPath("$[0].granted_by").value(reportItem.getGrantedBy())) + .andExpect(jsonPath("$[0].granted_by_full_name").value(reportItem.getGrantedByFullName())); + + verify(reportService, times(1)).reportShared(null, null, null, null, true); } @DisplayName("Should get a report containing a list of bookings with an available recording") @Test void reportScheduledSuccess() throws Exception { var reportItem = new ScheduleReportDTO(); - reportItem.setScheduledFor(Timestamp.from(Instant.now())); - reportItem.setBookingCreatedAt(Timestamp.from(Instant.now())); + var timestamp = Timestamp.from(Instant.now()); + reportItem.setScheduledDate(DateTimeUtils.formatDate(timestamp)); reportItem.setCaseReference("ABC123"); - reportItem.setCaptureSessionUser("example@example.com"); - reportItem.setCourt("Example court"); - reportItem.setRegions(Set.of()); + reportItem.setCourt("Example Court"); + reportItem.setCounty("Example County"); + reportItem.setPostcode("AB1 2CD"); + reportItem.setRegion("Example Region"); + reportItem.setDateOfBooking(DateTimeUtils.formatDate(timestamp)); + reportItem.setUser("example@example.com"); when(reportService.reportScheduled()).thenReturn(List.of(reportItem)); - - mockMvc.perform(get("/reports/schedules")) + mockMvc.perform(get("/reports-v2/schedules")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$[0].scheduled_date").value(reportItem.getScheduledDate())) .andExpect(jsonPath("$[0].case_reference").value(reportItem.getCaseReference())) .andExpect(jsonPath("$[0].court").value(reportItem.getCourt())) - .andExpect(jsonPath("$[0].capture_session_user").value(reportItem.getCaptureSessionUser())); + .andExpect(jsonPath("$[0].county").value(reportItem.getCounty())) + .andExpect(jsonPath("$[0].postcode").value(reportItem.getPostcode())) + .andExpect(jsonPath("$[0].region").value(reportItem.getRegion())) + .andExpect(jsonPath("$[0].date_of_booking").value(reportItem.getDateOfBooking())) + .andExpect(jsonPath("$[0].user").value(reportItem.getUser())); } @DisplayName("Should get a report containing a list of completed capture sessions (with available recordings)") @Test void reportCompletedCaptureSessions() throws Exception { var reportItem = new CompletedCaptureSessionReportDTO(); - reportItem.setStartedAt(Timestamp.from(Instant.now())); - reportItem.setFinishedAt(Timestamp.from(Instant.now())); - reportItem.setDuration(Duration.ofMinutes(3L)); - reportItem.setScheduledFor(Timestamp.from(Instant.now())); + var timestamp = Timestamp.from(Instant.now()); + reportItem.setRecordingDate(DateTimeUtils.formatDate(timestamp)); + reportItem.setRecordingTime(DateTimeUtils.formatTime(timestamp)); + reportItem.setFinishTime(DateTimeUtils.formatTime(timestamp)); + reportItem.setTimezone(DateTimeUtils.getTimezoneAbbreviation(timestamp)); + reportItem.setScheduledDate(DateTimeUtils.formatDate(timestamp)); reportItem.setCaseReference("ABC123"); - reportItem.setCountDefendants(1); - reportItem.setCountWitnesses(5); - reportItem.setRecordingStatus(RecordingStatus.RECORDING_AVAILABLE); + reportItem.setStatus(RecordingStatus.RECORDING_AVAILABLE); + reportItem.setDefendantNames("Defendant Name"); + reportItem.setDefendant(1); + reportItem.setWitnessNames("Witness Name"); + reportItem.setWitness(1); reportItem.setCourt("Example Court"); - reportItem.setRegions(Set.of()); + reportItem.setCounty("Example County"); + reportItem.setPostcode("AB1 2CD"); + reportItem.setRegion("Example Region"); when(reportService.reportCompletedCaptureSessions()).thenReturn(List.of(reportItem)); - mockMvc.perform(get("/reports/completed-capture-sessions")) + mockMvc.perform(get("/reports-v2/completed-capture-sessions")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$[0].duration").value(reportItem.getDuration().toString())) + .andExpect(jsonPath("$[0].recording_date").value(reportItem.getRecordingDate())) + .andExpect(jsonPath("$[0].recording_time").value(reportItem.getRecordingTime())) + .andExpect(jsonPath("$[0].finish_time").value(reportItem.getFinishTime())) + .andExpect(jsonPath("$[0].timezone").value(reportItem.getTimezone())) + .andExpect(jsonPath("$[0].scheduled_date").value(reportItem.getScheduledDate())) .andExpect(jsonPath("$[0].case_reference").value(reportItem.getCaseReference())) - .andExpect(jsonPath("$[0].count_defendants").value(reportItem.getCountDefendants())) - .andExpect(jsonPath("$[0].count_witnesses").value(reportItem.getCountWitnesses())) - .andExpect(jsonPath("$[0].recording_status").value(reportItem.getRecordingStatus().toString())) - .andExpect(jsonPath("$[0].court").value(reportItem.getCourt())); + .andExpect(jsonPath("$[0].status").value(reportItem.getStatus().toString())) + .andExpect(jsonPath("$[0].defendant_names").value(reportItem.getDefendantNames())) + .andExpect(jsonPath("$[0].defendant").value(reportItem.getDefendant())) + .andExpect(jsonPath("$[0].witness_names").value(reportItem.getWitnessNames())) + .andExpect(jsonPath("$[0].witness").value(reportItem.getWitness())) + .andExpect(jsonPath("$[0].court").value(reportItem.getCourt())) + .andExpect(jsonPath("$[0].county").value(reportItem.getCounty())) + .andExpect(jsonPath("$[0].postcode").value(reportItem.getPostcode())) + .andExpect(jsonPath("$[0].region").value(reportItem.getRegion())); } @DisplayName("Should get a report containing a list of share booking removals with case and user details") @Test void reportAccessRemoved() throws Exception { var reportItem = new AccessRemovedReportDTO(); - reportItem.setRemovedAt(Timestamp.from(Instant.now())); + var timestamp = Timestamp.from(Instant.now()); + reportItem.setRemovedDate(DateTimeUtils.formatDate(timestamp)); + reportItem.setRemovedTime(DateTimeUtils.formatTime(timestamp)); + reportItem.setRemovedTimezone(DateTimeUtils.getTimezoneAbbreviation(timestamp)); reportItem.setCaseReference("ABC123"); reportItem.setCourt("Example court"); - reportItem.setRegions(Set.of()); - reportItem.setUserFullName("Example Person"); + reportItem.setCounty("Kent"); + reportItem.setPostcode("AB1 2CD"); + reportItem.setRegion("Somewhere"); + reportItem.setFullName("Example Person"); reportItem.setUserEmail("example@example.com"); - reportItem.setRemovalReason("Example reason"); when(reportService.reportAccessRemoved()).thenReturn(List.of(reportItem)); - mockMvc.perform(get("/reports/share-bookings-removed")) + mockMvc.perform(get("/reports-v2/share-bookings-removed")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$[0].removed_date").value(reportItem.getRemovedDate())) + .andExpect(jsonPath("$[0].removed_time").value(reportItem.getRemovedTime())) + .andExpect(jsonPath("$[0].removed_timezone").value(reportItem.getRemovedTimezone())) .andExpect(jsonPath("$[0].case_reference").value(reportItem.getCaseReference())) .andExpect(jsonPath("$[0].court").value(reportItem.getCourt())) - .andExpect(jsonPath("$[0].user_full_name").value(reportItem.getUserFullName())) - .andExpect(jsonPath("$[0].user_email").value(reportItem.getUserEmail())) - .andExpect(jsonPath("$[0].removal_reason").value(reportItem.getRemovalReason())); + .andExpect(jsonPath("$[0].county").value(reportItem.getCounty())) + .andExpect(jsonPath("$[0].postcode").value(reportItem.getPostcode())) + .andExpect(jsonPath("$[0].full_name").value(reportItem.getFullName())) + .andExpect(jsonPath("$[0].user_email").value(reportItem.getUserEmail())); } @DisplayName("Should get a report containing a list of playback data for source 'PORTAL'") @Test void reportPlaybackPortalSuccess() throws Exception { - var reportItem = createPlaybackReport(); + var reportItem = createPlaybackReport(Timestamp.valueOf("2025-01-01 00:00:00")); when(reportService.reportPlayback(AuditLogSource.PORTAL)).thenReturn(List.of(reportItem)); - mockMvc.perform(get("/reports/playback") + mockMvc.perform(get("/reports-v2/playback") .param("source", "PORTAL")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$[0].playback_date").value("01/01/2025")) + .andExpect(jsonPath("$[0].playback_time").value("00:00:00")) + .andExpect(jsonPath("$[0].playback_time_zone").value("GMT")) .andExpect(jsonPath("$[0].user_full_name").value(reportItem.getUserFullName())) .andExpect(jsonPath("$[0].user_email").value(reportItem.getUserEmail())) + .andExpect(jsonPath("$[0].user_organisation").value(reportItem.getUserOrganisation())) .andExpect(jsonPath("$[0].case_reference").value(reportItem.getCaseReference())) .andExpect(jsonPath("$[0].court").value(reportItem.getCourt())) - .andExpect(jsonPath("$[0].recording_id").value(reportItem.getRecordingId().toString())); + .andExpect(jsonPath("$[0].county").value(reportItem.getCounty())) + .andExpect(jsonPath("$[0].postcode").value(reportItem.getPostcode())) + .andExpect(jsonPath("$[0].region").value(reportItem.getRegion())); verify(reportService, times(1)).reportPlayback(AuditLogSource.PORTAL); } @@ -364,19 +422,25 @@ void reportPlaybackPortalSuccess() throws Exception { @DisplayName("Should get a report containing a list of playback data for source 'APPLICATION'") @Test void reportPlaybackApplicationSuccess() throws Exception { - var reportItem = createPlaybackReport(); + var reportItem = createPlaybackReport(Timestamp.valueOf("2025-07-01 00:00:00")); when(reportService.reportPlayback(AuditLogSource.APPLICATION)).thenReturn(List.of(reportItem)); - mockMvc.perform(get("/reports/playback") + mockMvc.perform(get("/reports-v2/playback") .param("source", "APPLICATION")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$[0].user_full_name").value(reportItem.getUserFullName())) - .andExpect(jsonPath("$[0].user_email").value(reportItem.getUserEmail())) - .andExpect(jsonPath("$[0].case_reference").value(reportItem.getCaseReference())) - .andExpect(jsonPath("$[0].court").value(reportItem.getCourt())) - .andExpect(jsonPath("$[0].recording_id").value(reportItem.getRecordingId().toString())); + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$[0].playback_date").value("01/07/2025")) + .andExpect(jsonPath("$[0].playback_time").value("01:00:00")) + .andExpect(jsonPath("$[0].playback_time_zone").value("BST")) + .andExpect(jsonPath("$[0].user_full_name").value(reportItem.getUserFullName())) + .andExpect(jsonPath("$[0].user_email").value(reportItem.getUserEmail())) + .andExpect(jsonPath("$[0].user_organisation").value(reportItem.getUserOrganisation())) + .andExpect(jsonPath("$[0].case_reference").value(reportItem.getCaseReference())) + .andExpect(jsonPath("$[0].court").value(reportItem.getCourt())) + .andExpect(jsonPath("$[0].county").value(reportItem.getCounty())) + .andExpect(jsonPath("$[0].postcode").value(reportItem.getPostcode())) + .andExpect(jsonPath("$[0].region").value(reportItem.getRegion())); verify(reportService, times(1)).reportPlayback(AuditLogSource.APPLICATION); } @@ -384,55 +448,93 @@ void reportPlaybackApplicationSuccess() throws Exception { @DisplayName("Should get a report containing a list of playback data for no source") @Test void reportPlaybackAllSuccess() throws Exception { - var reportItem = createPlaybackReport(); + var reportItem = createPlaybackReport(Timestamp.valueOf("2025-01-01 00:00:00")); when(reportService.reportPlayback(null)).thenReturn(List.of(reportItem)); - mockMvc.perform(get("/reports/playback")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$[0].user_full_name").value(reportItem.getUserFullName())) - .andExpect(jsonPath("$[0].user_email").value(reportItem.getUserEmail())) - .andExpect(jsonPath("$[0].case_reference").value(reportItem.getCaseReference())) - .andExpect(jsonPath("$[0].court").value(reportItem.getCourt())) - .andExpect(jsonPath("$[0].recording_id").value(reportItem.getRecordingId().toString())); + mockMvc.perform(get("/reports-v2/playback")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$[0].playback_date").value("01/01/2025")) + .andExpect(jsonPath("$[0].playback_time").value("00:00:00")) + .andExpect(jsonPath("$[0].playback_time_zone").value("GMT")) + .andExpect(jsonPath("$[0].user_full_name").value(reportItem.getUserFullName())) + .andExpect(jsonPath("$[0].user_email").value(reportItem.getUserEmail())) + .andExpect(jsonPath("$[0].user_organisation").value(reportItem.getUserOrganisation())) + .andExpect(jsonPath("$[0].case_reference").value(reportItem.getCaseReference())) + .andExpect(jsonPath("$[0].court").value(reportItem.getCourt())) + .andExpect(jsonPath("$[0].county").value(reportItem.getCounty())) + .andExpect(jsonPath("$[0].postcode").value(reportItem.getPostcode())) + .andExpect(jsonPath("$[0].region").value(reportItem.getRegion())); verify(reportService, times(1)).reportPlayback(null); } - @DisplayName("Should get a report containing a list of participants and their related recordings") - @Test - void reportRecordingParticipantsSuccess() throws Exception { - var dto = new RecordingParticipantsReportDTO(); - dto.setParticipantName("Participant Name"); - dto.setParticipantType(ParticipantType.WITNESS); - dto.setRecordedAt(Timestamp.from(Instant.now())); - dto.setCourtName("Court Name"); - dto.setCaseReference("1234567890"); - dto.setRecordingId(UUID.randomUUID()); - - when(reportService.reportRecordingParticipants()).thenReturn(List.of(dto)); - - mockMvc.perform(get("/reports/recording-participants")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(jsonPath("$[0].participant_name").value(dto.getParticipantName())) - .andExpect(jsonPath("$[0].participant_type").value(dto.getParticipantType().toString())) - .andExpect(jsonPath("$[0].court_name").value(dto.getCourtName())) - .andExpect(jsonPath("$[0].case_reference").value(dto.getCaseReference())) - .andExpect(jsonPath("$[0].recording_id").value(dto.getRecordingId().toString())); + private PlaybackReportDTO createPlaybackReport(Timestamp createdAt) { + var user = new User(); + user.setId(UUID.randomUUID()); + user.setOrganisation("FooOrg"); + user.setFirstName("Example"); + user.setLastName("Person"); + + var regionEntity = new Region(); + regionEntity.setId(UUID.randomUUID()); + regionEntity.setName("London"); + + var regionEntity2 = new Region(); + regionEntity2.setId(UUID.randomUUID()); + regionEntity2.setName("Manchester"); + + var courtEntity = new Court(); + courtEntity.setId(UUID.randomUUID()); + courtEntity.setName("Example Court"); + courtEntity.setRegions(Set.of(regionEntity, regionEntity2)); + courtEntity.setCounty("Kent"); + courtEntity.setPostcode("AB1 2CD"); + + var recordingEntity = new Recording(); + recordingEntity.setId(UUID.randomUUID()); + + var caseEntity = new Case(); + caseEntity.setId(UUID.randomUUID()); + caseEntity.setCourt(courtEntity); + caseEntity.setReference("ABC123"); + + var bookingEntity = new Booking(); + bookingEntity.setId(UUID.randomUUID()); + bookingEntity.setCaseId(caseEntity); + + var captureSessionEntity = new CaptureSession(); + captureSessionEntity.setId(UUID.randomUUID()); + captureSessionEntity.setBooking(bookingEntity); + + recordingEntity.setCaptureSession(captureSessionEntity); + + var auditEntity = new Audit(); + auditEntity.setId(UUID.randomUUID()); + auditEntity.setCreatedAt(createdAt); + auditEntity.setTableRecordId(recordingEntity.getId()); + + return new PlaybackReportDTO(auditEntity, user, null); } - private PlaybackReportDTO createPlaybackReport() { - return new PlaybackReportDTO( - Timestamp.from(Instant.now()), - "Example Person", - "example@example.com", - "CASE123456", - "Example Court", - Set.of(), - UUID.randomUUID() - ); + private SharedReportDTO createSharedReport() { + var reportItem = new SharedReportDTO(); + var timestamp = Timestamp.from(Instant.now()); + reportItem.setShareDate(DateTimeUtils.formatDate(timestamp)); + reportItem.setShareTime(DateTimeUtils.formatTime(timestamp)); + reportItem.setTimezone(DateTimeUtils.getTimezoneAbbreviation(timestamp)); + reportItem.setSharedWith("shared-with@example.com"); + reportItem.setSharedWithFullName("Example One"); + reportItem.setOrganisationSharedWith("Example Organisation"); + reportItem.setGrantedBy("shared-by@example.com"); + reportItem.setGrantedByFullName("Example Two"); + reportItem.setCaseReference("ABC123"); + reportItem.setCourt("Example Court"); + reportItem.setCounty("Example County"); + reportItem.setPostcode("AB1 2CD"); + reportItem.setRegion("Example Region"); + return reportItem; } @DisplayName("Should get a report containing a list of users, their first and last names and their related " @@ -449,7 +551,7 @@ void reportUserPrimaryCourtsSuccess() throws Exception { when(reportService.reportUserPrimaryCourts()).thenReturn(List.of(dto)); - mockMvc.perform(get("/reports/user-primary-courts")) + mockMvc.perform(get("/reports-v2/user-primary-courts")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) .andExpect(jsonPath("$[0].first_name").value(dto.getFirstName())) diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/services/LegacyReportServiceTest.java b/src/test/java/uk/gov/hmcts/reform/preapi/services/LegacyReportServiceTest.java new file mode 100644 index 000000000..8fe046bf2 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/preapi/services/LegacyReportServiceTest.java @@ -0,0 +1,748 @@ +package uk.gov.hmcts.reform.preapi.services; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import uk.gov.hmcts.reform.preapi.entities.AppAccess; +import uk.gov.hmcts.reform.preapi.entities.Audit; +import uk.gov.hmcts.reform.preapi.entities.Booking; +import uk.gov.hmcts.reform.preapi.entities.CaptureSession; +import uk.gov.hmcts.reform.preapi.entities.Case; +import uk.gov.hmcts.reform.preapi.entities.Court; +import uk.gov.hmcts.reform.preapi.entities.Participant; +import uk.gov.hmcts.reform.preapi.entities.PortalAccess; +import uk.gov.hmcts.reform.preapi.entities.Recording; +import uk.gov.hmcts.reform.preapi.entities.Region; +import uk.gov.hmcts.reform.preapi.entities.ShareBooking; +import uk.gov.hmcts.reform.preapi.entities.User; +import uk.gov.hmcts.reform.preapi.enums.AuditLogSource; +import uk.gov.hmcts.reform.preapi.enums.ParticipantType; +import uk.gov.hmcts.reform.preapi.enums.RecordingStatus; +import uk.gov.hmcts.reform.preapi.exception.NotFoundException; +import uk.gov.hmcts.reform.preapi.repositories.AppAccessRepository; +import uk.gov.hmcts.reform.preapi.repositories.AuditRepository; +import uk.gov.hmcts.reform.preapi.repositories.CaptureSessionRepository; +import uk.gov.hmcts.reform.preapi.repositories.PortalAccessRepository; +import uk.gov.hmcts.reform.preapi.repositories.RecordingRepository; +import uk.gov.hmcts.reform.preapi.repositories.ShareBookingRepository; +import uk.gov.hmcts.reform.preapi.repositories.UserRepository; + +import java.sql.Timestamp; +import java.time.Duration; +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@SpringBootTest(classes = LegacyReportService.class) +public class LegacyReportServiceTest { + private static Recording recordingEntity; + private static CaptureSession captureSessionEntity; + private static Court courtEntity; + private static Region regionEntity; + private static Case caseEntity; + private static Booking bookingEntity; + private static Audit auditEntity; + + @MockitoBean + private CaptureSessionRepository captureSessionRepository; + + @MockitoBean + private RecordingRepository recordingRepository; + + @MockitoBean + private ShareBookingRepository shareBookingRepository; + + @MockitoBean + private AuditRepository auditRepository; + + @MockitoBean + private UserRepository userRepository; + + @MockitoBean + private AppAccessRepository appAccessRepository; + + @MockitoBean + private PortalAccessRepository portalAccessRepository; + + @Autowired + private LegacyReportService reportService; + + @BeforeAll + static void setUp() { + regionEntity = new Region(); + regionEntity.setId(UUID.randomUUID()); + regionEntity.setName("London"); + + courtEntity = new Court(); + courtEntity.setId(UUID.randomUUID()); + courtEntity.setName("Example Court"); + courtEntity.setRegions(Set.of(regionEntity)); + + recordingEntity = new Recording(); + recordingEntity.setId(UUID.randomUUID()); + + caseEntity = new Case(); + caseEntity.setId(UUID.randomUUID()); + caseEntity.setCourt(courtEntity); + caseEntity.setReference("ABC123"); + + bookingEntity = new Booking(); + bookingEntity.setId(UUID.randomUUID()); + bookingEntity.setCaseId(caseEntity); + + captureSessionEntity = new CaptureSession(); + captureSessionEntity.setId(UUID.randomUUID()); + captureSessionEntity.setBooking(bookingEntity); + + recordingEntity.setCaptureSession(captureSessionEntity); + recordingEntity.setVersion(1); + recordingEntity.setFilename("example-filename.txt"); + recordingEntity.setCreatedAt(Timestamp.from(Instant.now())); + + auditEntity = new Audit(); + auditEntity.setId(UUID.randomUUID()); + auditEntity.setCreatedAt(Timestamp.from(Instant.now())); + auditEntity.setTableRecordId(recordingEntity.getId()); + } + + + @BeforeEach + void reset() { + captureSessionEntity.setStartedAt(Timestamp.from(Instant.now())); + captureSessionEntity.setFinishedAt(Timestamp.from(Instant.now())); + captureSessionEntity.setStatus(null); + recordingEntity.setDuration(null); + recordingEntity.setVersion(1); + recordingEntity.setParentRecording(null); + auditEntity.setSource(null); + auditEntity.setCreatedBy(null); + auditEntity.setAuditDetails(null); + bookingEntity.setParticipants(Set.of()); + } + + @DisplayName("Find all capture sessions and return a list of models as a report when capture session is incomplete") + @Test + void captureSessionReportCaptureSessionIncompleteSuccess() { + captureSessionEntity.setStartedAt(Timestamp.from(Instant.now())); + captureSessionEntity.setFinishedAt(Timestamp.from(Instant.now())); + captureSessionEntity.setRecordings(Set.of()); + when(captureSessionRepository.reportConcurrentCaptureSessions()).thenReturn(List.of(captureSessionEntity)); + + var report = reportService.reportCaptureSessions(); + + verify(captureSessionRepository, times(1)).reportConcurrentCaptureSessions(); + + assertThat(report.size()).isEqualTo(1); + var first = report.getFirst(); + + assertThat(first.getId()).isEqualTo(captureSessionEntity.getId()); + assertThat(first.getStartTime()).isEqualTo(captureSessionEntity.getStartedAt()); + assertThat(first.getEndTime()).isEqualTo(captureSessionEntity.getFinishedAt()); + assertThat(first.getDuration()).isNull(); + assertThat(first.getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(first.getCourt()).isEqualTo(courtEntity.getName()); + assertThat(first.getRegion().stream().findFirst().isPresent()).isTrue(); + assertThat(first.getRegion().stream().findFirst().get().getName()).isEqualTo(regionEntity.getName()); + } + + @DisplayName("Find all capture sessions and return a list of models as a report on concurrent capture sessions") + @Test + void captureSessionReportConcurrentCaptureSessionsSuccess() { + recordingEntity.setDuration(Duration.ofMinutes(3)); + captureSessionEntity.setStartedAt(Timestamp.from(Instant.now())); + captureSessionEntity.setFinishedAt(Timestamp.from(Instant.now())); + captureSessionEntity.setRecordings(Set.of(recordingEntity)); + when(captureSessionRepository.reportConcurrentCaptureSessions()).thenReturn(List.of(captureSessionEntity)); + + var report = reportService.reportCaptureSessions(); + + verify(captureSessionRepository, times(1)).reportConcurrentCaptureSessions(); + + assertThat(report.size()).isEqualTo(1); + var first = report.getFirst(); + + assertThat(first.getId()).isEqualTo(captureSessionEntity.getId()); + assertThat(first.getStartTime()).isEqualTo(captureSessionEntity.getStartedAt()); + assertThat(first.getEndTime()).isEqualTo(captureSessionEntity.getFinishedAt()); + assertThat(first.getDuration()).isEqualTo(recordingEntity.getDuration()); + assertThat(first.getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(first.getCourt()).isEqualTo(courtEntity.getName()); + assertThat(first.getRegion().stream().findFirst().isPresent()).isTrue(); + assertThat(first.getRegion().stream().findFirst().get().getName()).isEqualTo(regionEntity.getName()); + } + + @DisplayName("Find counts for recordings per case an return a report list") + @Test + void reportRecordingsPerCaseSuccess() { + var anotherCase = new Case(); + anotherCase.setId(UUID.randomUUID()); + anotherCase.setCourt(courtEntity); + anotherCase.setReference("XYZ456"); + + when(recordingRepository.countRecordingsPerCase()) + .thenReturn(List.of(new Object[] { caseEntity, (long) 1 }, new Object[]{ anotherCase, (long) 0 })); + + var report = reportService.reportRecordingsPerCase(); + + assertThat(report.size()).isEqualTo(2); + assertThat(report.get(0).getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(report.get(0).getCount()).isEqualTo(1); + assertThat(report.get(1).getCaseReference()).isEqualTo(anotherCase.getReference()); + assertThat(report.get(1).getCount()).isEqualTo(0); + + assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); + assertThat(report + .getFirst() + .getRegions() + .stream() + .toList() + .getFirst() + .getName() + ).isEqualTo(regionEntity.getName()); + } + + @DisplayName("Find all edited recordings and return a report list") + @Test + void reportEditsSuccess() { + recordingEntity.setVersion(2); + var recording2 = new Recording(); + recording2.setId(UUID.randomUUID()); + recording2.setVersion(3); + recording2.setCreatedAt(Timestamp.from(Instant.MIN)); + recording2.setCaptureSession(captureSessionEntity); + + when(recordingRepository.findAllByParentRecordingIsNotNull()).thenReturn(List.of(recording2, recordingEntity)); + + var report = reportService.reportEdits(); + + assertThat(report.size()).isEqualTo(2); + + assertThat(report.getFirst().getCreatedAt()).isEqualTo(recordingEntity.getCreatedAt()); + assertThat(report.getFirst().getVersion()).isEqualTo(recordingEntity.getVersion()); + assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); + assertThat(report + .getFirst() + .getRegions() + .stream() + .toList() + .getFirst() + .getName() + ).isEqualTo(regionEntity.getName()); + assertThat(report.getFirst().getRecordingId()).isEqualTo(recordingEntity.getId()); + + assertThat(report.getLast().getRecordingId()).isEqualTo(recording2.getId()); + } + + @DisplayName("Find shared bookings and return report list") + @Test + void reportShared() { + var shareWith = new User(); + shareWith.setId(UUID.randomUUID()); + shareWith.setEmail("example1@example.com"); + + var shareBy = new User(); + shareBy.setId(UUID.randomUUID()); + shareBy.setEmail("example2@example.com"); + + var sharedBooking1 = new ShareBooking(); + sharedBooking1.setCreatedAt(Timestamp.from(Instant.MIN)); + sharedBooking1.setBooking(bookingEntity); + sharedBooking1.setSharedWith(shareWith); + sharedBooking1.setSharedBy(shareBy); + + var sharedBooking2 = new ShareBooking(); + sharedBooking2.setCreatedAt(Timestamp.from(Instant.now())); + sharedBooking2.setBooking(bookingEntity); + sharedBooking2.setSharedWith(shareWith); + sharedBooking2.setSharedBy(shareBy); + + when(shareBookingRepository.searchAll(null, null, null, null, false)) + .thenReturn(List.of(sharedBooking1, sharedBooking2)); + + var report = reportService.reportShared(null, null, null, null); + + assertThat(report.size()).isEqualTo(2); + + assertThat(report.getFirst().getSharedAt()).isEqualTo(sharedBooking2.getCreatedAt()); + assertThat(report.getFirst().getAllocatedTo()).isEqualTo(sharedBooking2.getSharedWith().getEmail()); + assertThat(report.getFirst().getAllocatedBy()).isEqualTo(sharedBooking2.getSharedBy().getEmail()); + assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); + assertThat(report + .getFirst() + .getRegions() + .stream() + .toList() + .getFirst() + .getName() + ).isEqualTo(regionEntity.getName()); + assertThat(report.getFirst().getBookingId()).isEqualTo(sharedBooking2.getBooking().getId()); + + assertThat(report.getLast().getSharedAt()).isEqualTo(sharedBooking1.getCreatedAt()); + } + + @DisplayName("Find all capture sessions with recording available and get report on booking details") + @Test + void reportScheduledSuccess() { + var userEntity = new User(); + userEntity.setId(UUID.randomUUID()); + userEntity.setEmail("example@example.com"); + captureSessionEntity.setStatus(RecordingStatus.RECORDING_AVAILABLE); + captureSessionEntity.setStartedByUser(userEntity); + + var otherBooking = new Booking(); + otherBooking.setId(UUID.randomUUID()); + otherBooking.setCaseId(caseEntity); + otherBooking.setScheduledFor(Timestamp.from(Instant.MIN)); + + captureSessionEntity.getBooking().setScheduledFor(Timestamp.from(Instant.MAX)); + + var otherCaptureSessionEntity = new CaptureSession(); + otherCaptureSessionEntity.setId(UUID.randomUUID()); + otherCaptureSessionEntity.setBooking(otherBooking); + otherCaptureSessionEntity.setStatus(RecordingStatus.RECORDING_AVAILABLE); + otherCaptureSessionEntity.setStartedByUser(userEntity); + + when(captureSessionRepository.findAllByStatus(RecordingStatus.RECORDING_AVAILABLE)) + .thenReturn(List.of(otherCaptureSessionEntity, captureSessionEntity)); + + var report = reportService.reportScheduled(); + + assertThat(report.size()).isEqualTo(2); + assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(report.getFirst().getScheduledFor()).isEqualTo(captureSessionEntity.getBooking().getScheduledFor()); + assertThat(report.getFirst().getCaptureSessionUser()).isEqualTo(userEntity.getEmail()); + + assertThat(report.get(1).getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(report.get(1).getScheduledFor()).isEqualTo(otherBooking.getScheduledFor()); + } + + @DisplayName("Find audits relating to playbacks from the portal and return a report") + @Test + void reportPlaybackPortalSuccess() { + var user = new User(); + user.setId(UUID.randomUUID()); + user.setEmail("example@example.com"); + user.setFirstName("Example"); + user.setLastName("Person"); + auditEntity.setCreatedBy(user.getId()); + auditEntity.setSource(AuditLogSource.PORTAL); + + var objectMapper = new ObjectMapper(); + var objectNode = objectMapper.createObjectNode(); + objectNode.put("recordingId", recordingEntity.getId().toString()); + auditEntity.setAuditDetails(objectNode); + + when(auditRepository + .findBySourceAndFunctionalAreaAndActivity( + AuditLogSource.PORTAL, + "Video Player", + "Play" + ) + ).thenReturn(List.of(auditEntity)); + when(userRepository.findById(user.getId())).thenReturn(Optional.of(user)); + when(recordingRepository.findById(recordingEntity.getId())).thenReturn(Optional.of(recordingEntity)); + + var report = reportService.reportPlayback(AuditLogSource.PORTAL); + + assertThat(report.size()).isEqualTo(1); + assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); + assertThat(report.getFirst().getUserEmail()).isEqualTo(user.getEmail()); + assertThat(report.getFirst().getUserFullName()).isEqualTo(user.getFullName()); + assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); + assertThat(report.getFirst().getRecordingId()).isEqualTo(recordingEntity.getId()); + assertThat(report + .getFirst() + .getRegions() + .stream() + .toList() + .getFirst() + .getName() + ).isEqualTo(regionEntity.getName()); + } + + @DisplayName("Find audits relating to playbacks from the portal and return a report using erroneous recordinguid") + @Test + void reportPlaybackPortalSuccessRecordingid() { + auditEntity.setSource(AuditLogSource.PORTAL); + + var objectMapper = new ObjectMapper(); + var objectNode = objectMapper.createObjectNode(); + objectNode.put("recordinguid", recordingEntity.getId().toString()); + auditEntity.setAuditDetails(objectNode); + + when(auditRepository + .findBySourceAndFunctionalAreaAndActivity( + AuditLogSource.PORTAL, + "Video Player", + "Play" + ) + ).thenReturn(List.of(auditEntity)); + when(recordingRepository.findById(recordingEntity.getId())).thenReturn(Optional.of(recordingEntity)); + + var report = reportService.reportPlayback(AuditLogSource.PORTAL); + + assertThat(report.size()).isEqualTo(1); + assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); + assertThat(report.getFirst().getUserEmail()).isNullOrEmpty(); + assertThat(report.getFirst().getUserFullName()).isNullOrEmpty(); + assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); + assertThat(report.getFirst().getRecordingId()).isEqualTo(recordingEntity.getId()); + assertThat(report + .getFirst() + .getRegions() + .stream() + .toList() + .getFirst() + .getName() + ).isEqualTo(regionEntity.getName()); + } + + @DisplayName("Find audits relating to playbacks from the portal and return a report without recordingid") + @Test + void reportPlaybackPortalSuccessNoRecordingId() { + auditEntity.setSource(AuditLogSource.PORTAL); + + var objectMapper = new ObjectMapper(); + var objectNode = objectMapper.createObjectNode(); + auditEntity.setAuditDetails(objectNode); + + when(auditRepository + .findBySourceAndFunctionalAreaAndActivity( + AuditLogSource.PORTAL, + "Video Player", + "Play" + ) + ).thenReturn(List.of(auditEntity)); + when(recordingRepository.findById(recordingEntity.getId())).thenReturn(Optional.of(recordingEntity)); + + var report = reportService.reportPlayback(AuditLogSource.PORTAL); + + assertThat(report.size()).isEqualTo(1); + assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); + assertThat(report.getFirst().getUserEmail()).isNullOrEmpty(); + assertThat(report.getFirst().getUserFullName()).isNullOrEmpty(); + assertThat(report.getFirst().getCaseReference()).isNullOrEmpty(); + assertThat(report.getFirst().getCourt()).isNullOrEmpty(); + assertThat(report.getFirst().getRecordingId()).isNull(); + } + + @DisplayName("Find audits relating to playbacks from the portal and return a report without audit details") + @Test + void reportPlaybackPortalSuccessNoAuditDetails() { + auditEntity.setSource(AuditLogSource.PORTAL); + + when(auditRepository + .findBySourceAndFunctionalAreaAndActivity( + AuditLogSource.PORTAL, + "Video Player", + "Play" + ) + ).thenReturn(List.of(auditEntity)); + var report = reportService.reportPlayback(AuditLogSource.PORTAL); + + assertThat(report.size()).isEqualTo(1); + assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); + assertThat(report.getFirst().getUserEmail()).isNullOrEmpty(); + assertThat(report.getFirst().getUserFullName()).isNullOrEmpty(); + assertThat(report.getFirst().getCaseReference()).isNullOrEmpty(); // has a value??? + assertThat(report.getFirst().getCourt()).isNullOrEmpty(); + assertThat(report.getFirst().getRecordingId()).isNull(); + } + + @DisplayName("Find audits relating to playbacks from the application and return a report") + @Test + void reportPlaybackApplicationSuccess() { + var user = new User(); + user.setId(UUID.randomUUID()); + user.setEmail("example@example.com"); + user.setFirstName("Example"); + user.setLastName("Person"); + auditEntity.setCreatedBy(user.getId()); + auditEntity.setSource(AuditLogSource.APPLICATION); + + var objectMapper = new ObjectMapper(); + var objectNode = objectMapper.createObjectNode(); + objectNode.put("recordingId", recordingEntity.getId().toString()); + auditEntity.setAuditDetails(objectNode); + + when(auditRepository + .findBySourceAndFunctionalAreaAndActivity( + AuditLogSource.APPLICATION, + "View Recordings", + "Play" + ) + ).thenReturn(List.of(auditEntity)); + when(userRepository.findById(user.getId())).thenReturn(Optional.of(user)); + when(recordingRepository.findById(recordingEntity.getId())).thenReturn(Optional.of(recordingEntity)); + + var report = reportService.reportPlayback(AuditLogSource.APPLICATION); + + assertThat(report.size()).isEqualTo(1); + assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); + assertThat(report.getFirst().getUserEmail()).isEqualTo(user.getEmail()); + assertThat(report.getFirst().getUserFullName()).isEqualTo(user.getFullName()); + assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); + assertThat(report.getFirst().getRecordingId()).isEqualTo(recordingEntity.getId()); + assertThat(report + .getFirst() + .getRegions() + .stream() + .toList() + .getFirst() + .getName() + ).isEqualTo(regionEntity.getName()); + } + + @DisplayName("Find audits relating to all playback attempts and return a report") + @Test + void reportPlaybackAllSuccess() { + var user = new User(); + user.setId(UUID.randomUUID()); + user.setEmail("example@example.com"); + user.setFirstName("Example"); + user.setLastName("Person"); + auditEntity.setCreatedBy(user.getId()); + auditEntity.setSource(AuditLogSource.APPLICATION); + var objectMapper = new ObjectMapper(); + var objectNode = objectMapper.createObjectNode(); + objectNode.put("recordingId", recordingEntity.getId().toString()); + auditEntity.setAuditDetails(objectNode); + + when(auditRepository.findAllAccessAttempts()).thenReturn(List.of(auditEntity)); + when(userRepository.findById(user.getId())).thenReturn(Optional.of(user)); + when(recordingRepository.findById(recordingEntity.getId())).thenReturn(Optional.of(recordingEntity)); + + var report = reportService.reportPlayback(null); + + assertThat(report.size()).isEqualTo(1); + assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); + assertThat(report.getFirst().getUserEmail()).isEqualTo(user.getEmail()); + assertThat(report.getFirst().getUserFullName()).isEqualTo(user.getFullName()); + assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); + assertThat(report.getFirst().getRecordingId()).isEqualTo(recordingEntity.getId()); + assertThat(report + .getFirst() + .getRegions() + .stream() + .toList() + .getFirst() + .getName() + ).isEqualTo(regionEntity.getName()); + + verify(auditRepository, times(1)).findAllAccessAttempts(); + verify(auditRepository, never()).findBySourceAndFunctionalAreaAndActivity(any(), any(), any()); + } + + @DisplayName("Find audits relating to all playback attempts and return a report when null auditdetails") + @Test + void reportPlaybackAllSuccessNullAuditDetails() { + var user = new User(); + user.setId(UUID.randomUUID()); + user.setEmail("example@example.com"); + user.setFirstName("Example"); + user.setLastName("Person"); + auditEntity.setCreatedBy(user.getId()); + auditEntity.setSource(AuditLogSource.APPLICATION); + + when(auditRepository.findAllAccessAttempts()).thenReturn(List.of(auditEntity)); + when(userRepository.findById(user.getId())).thenReturn(Optional.of(user)); + when(recordingRepository.findById(recordingEntity.getId())).thenReturn(Optional.of(recordingEntity)); + + var report = reportService.reportPlayback(null); + + assertThat(report.size()).isEqualTo(1); + assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); + assertThat(report.getFirst().getUserEmail()).isEqualTo(user.getEmail()); + assertThat(report.getFirst().getUserFullName()).isEqualTo(user.getFullName()); + assertThat(report.getFirst().getCaseReference()).isEqualTo(null); + assertThat(report.getFirst().getCourt()).isEqualTo(null); + assertThat(report.getFirst().getRecordingId()).isEqualTo(null); + + verify(auditRepository, times(1)).findAllAccessAttempts(); + verify(auditRepository, never()).findBySourceAndFunctionalAreaAndActivity(any(), any(), any()); + } + + @Test + @DisplayName("Returns audits for all playback attempts a report when createdBy is appAccess id not of user id") + void reportPlaybackAllSuccessAuditDetailsWhenAppAccessId() { + var user = new User(); + user.setId(UUID.randomUUID()); + user.setEmail("example@example.com"); + user.setFirstName("Example"); + user.setLastName("Person"); + var appAccess = new AppAccess(); + appAccess.setId(UUID.randomUUID()); + appAccess.setUser(user); + auditEntity.setCreatedBy(appAccess.getId()); + auditEntity.setSource(AuditLogSource.APPLICATION); + + when(auditRepository.findAllAccessAttempts()).thenReturn(List.of(auditEntity)); + when(userRepository.findById(user.getId())).thenReturn(Optional.empty()); + when(appAccessRepository.findById(appAccess.getId())).thenReturn(Optional.of(appAccess)); + when(recordingRepository.findById(recordingEntity.getId())).thenReturn(Optional.of(recordingEntity)); + + var report = reportService.reportPlayback(null); + + assertThat(report.size()).isEqualTo(1); + assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); + assertThat(report.getFirst().getUserEmail()).isEqualTo(user.getEmail()); + assertThat(report.getFirst().getUserFullName()).isEqualTo(user.getFullName()); + + verify(appAccessRepository, times(1)).findById(appAccess.getId()); + verify(auditRepository, times(1)).findAllAccessAttempts(); + verify(auditRepository, never()).findBySourceAndFunctionalAreaAndActivity(any(), any(), any()); + } + + @Test + @DisplayName("Returns audits for all playback attempts a report when createdBy is portalAccess id not of user id") + void reportPlaybackAllSuccessAuditDetailsWhenPortalAccessId() { + var user = new User(); + user.setId(UUID.randomUUID()); + user.setEmail("example@example.com"); + user.setFirstName("Example"); + user.setLastName("Person"); + var portalAccess = new PortalAccess(); + portalAccess.setId(UUID.randomUUID()); + portalAccess.setUser(user); + auditEntity.setCreatedBy(portalAccess.getId()); + auditEntity.setSource(AuditLogSource.APPLICATION); + + when(auditRepository.findAllAccessAttempts()).thenReturn(List.of(auditEntity)); + when(userRepository.findById(user.getId())).thenReturn(Optional.empty()); + when(appAccessRepository.findById(portalAccess.getId())).thenReturn(Optional.empty()); + when(portalAccessRepository.findById(portalAccess.getId())).thenReturn(Optional.of(portalAccess)); + when(recordingRepository.findById(recordingEntity.getId())).thenReturn(Optional.of(recordingEntity)); + + var report = reportService.reportPlayback(null); + + assertThat(report.size()).isEqualTo(1); + assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); + assertThat(report.getFirst().getUserEmail()).isEqualTo(user.getEmail()); + assertThat(report.getFirst().getUserFullName()).isEqualTo(user.getFullName()); + + verify(appAccessRepository, times(1)).findById(portalAccess.getId()); + verify(portalAccessRepository, times(1)).findById(portalAccess.getId()); + verify(auditRepository, times(1)).findAllAccessAttempts(); + verify(auditRepository, never()).findBySourceAndFunctionalAreaAndActivity(any(), any(), any()); + } + + @DisplayName("Find audits relating to playbacks from the source 'admin' and throw not found error") + @Test + void reportPlaybackAdminNotFound() { + assertThrows( + NotFoundException.class, + () -> reportService.reportPlayback(AuditLogSource.ADMIN) + ); + + verify(auditRepository, never()).findBySourceAndFunctionalAreaAndActivity(any(), any(), any()); + verify(userRepository, never()).findById(any()); + verify(recordingRepository, never()).findById(any()); + } + + @DisplayName("Find audits relating to playbacks from the source 'auto' and throw not found error") + @Test + void reportPlaybackAutoNotFound() { + assertThrows( + NotFoundException.class, + () -> reportService.reportPlayback(AuditLogSource.AUTO) + ); + + verify(auditRepository, never()).findBySourceAndFunctionalAreaAndActivity(any(), any(), any()); + verify(userRepository, never()).findById(any()); + verify(recordingRepository, never()).findById(any()); + } + + @DisplayName("Find a list of completed capture sessions") + @Test + void reportCompletedCaptureSessionsSuccess() { + captureSessionEntity.setStatus(RecordingStatus.RECORDING_AVAILABLE); + + final var witness = new Participant(); + witness.setId(UUID.randomUUID()); + witness.setParticipantType(ParticipantType.WITNESS); + witness.setCaseId(caseEntity); + final var defendant = new Participant(); + defendant.setId(UUID.randomUUID()); + defendant.setParticipantType(ParticipantType.DEFENDANT); + defendant.setCaseId(caseEntity); + + bookingEntity.setParticipants(Set.of(witness, defendant)); + + when(recordingRepository.findAllByParentRecordingIsNull()).thenReturn(List.of(recordingEntity)); + + var report = reportService.reportCompletedCaptureSessions(); + + assertThat(report.getFirst().getStartedAt()).isEqualTo(captureSessionEntity.getStartedAt()); + assertThat(report.getFirst().getFinishedAt()).isEqualTo(captureSessionEntity.getFinishedAt()); + assertThat(report.getFirst().getDuration()).isEqualTo(recordingEntity.getDuration()); + assertThat(report.getFirst().getScheduledFor()).isEqualTo(bookingEntity.getScheduledFor()); + assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(report.getFirst().getCountDefendants()).isEqualTo(1); + assertThat(report.getFirst().getCountWitnesses()).isEqualTo(1); + assertThat(report.getFirst().getRecordingStatus()).isEqualTo(captureSessionEntity.getStatus()); + assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); + assertThat(report + .getFirst() + .getRegions() + .stream() + .toList() + .getFirst() + .getName() + ).isEqualTo(regionEntity.getName()); + } + + @DisplayName("Find all share booking removals and return a report") + @Test + void reportAccessRemovedSuccess() { + var user = new User(); + user.setId(UUID.randomUUID()); + user.setFirstName("Example"); + user.setLastName("Person"); + user.setEmail("example@example.com"); + var shareBooking = new ShareBooking(); + shareBooking.setId(UUID.randomUUID()); + shareBooking.setSharedWith(user); + shareBooking.setBooking(bookingEntity); + shareBooking.setDeletedAt(Timestamp.from(Instant.now())); + + when(shareBookingRepository.findAllByDeletedAtIsNotNull()).thenReturn(List.of(shareBooking)); + + var report = reportService.reportAccessRemoved(); + + assertThat(report.getFirst().getRemovedAt()).isEqualTo(shareBooking.getDeletedAt()); + assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); + assertThat(report + .getFirst() + .getRegions() + .stream() + .toList() + .getFirst() + .getName() + ).isEqualTo(regionEntity.getName()); + assertThat(report.getFirst().getUserFullName()).isEqualTo(user.getFirstName() + " " + user.getLastName()); + assertThat(report.getFirst().getUserEmail()).isEqualTo(user.getEmail()); + assertThat(report.getFirst().getRemovalReason()).isNull(); + } +} diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/services/ReportServiceTest.java b/src/test/java/uk/gov/hmcts/reform/preapi/services/ReportServiceTest.java index 6dbb79a0d..d91a050fb 100644 --- a/src/test/java/uk/gov/hmcts/reform/preapi/services/ReportServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/preapi/services/ReportServiceTest.java @@ -32,6 +32,7 @@ import uk.gov.hmcts.reform.preapi.repositories.RecordingRepository; import uk.gov.hmcts.reform.preapi.repositories.ShareBookingRepository; import uk.gov.hmcts.reform.preapi.repositories.UserRepository; +import uk.gov.hmcts.reform.preapi.utils.DateTimeUtils; import java.sql.Timestamp; import java.time.Duration; @@ -93,6 +94,8 @@ static void setUp() { courtEntity.setId(UUID.randomUUID()); courtEntity.setName("Example Court"); courtEntity.setRegions(Set.of(regionEntity)); + courtEntity.setCounty("Kent"); + courtEntity.setPostcode("AB1 2CD"); recordingEntity = new Recording(); recordingEntity.setId(UUID.randomUUID()); @@ -121,7 +124,6 @@ static void setUp() { auditEntity.setTableRecordId(recordingEntity.getId()); } - @BeforeEach void reset() { captureSessionEntity.setStartedAt(Timestamp.from(Instant.now())); @@ -140,7 +142,7 @@ void reset() { @Test void captureSessionReportCaptureSessionIncompleteSuccess() { captureSessionEntity.setStartedAt(Timestamp.from(Instant.now())); - captureSessionEntity.setFinishedAt(Timestamp.from(Instant.now())); + captureSessionEntity.setFinishedAt(null); captureSessionEntity.setRecordings(Set.of()); when(captureSessionRepository.reportConcurrentCaptureSessions()).thenReturn(List.of(captureSessionEntity)); @@ -151,14 +153,15 @@ void captureSessionReportCaptureSessionIncompleteSuccess() { assertThat(report.size()).isEqualTo(1); var first = report.getFirst(); - assertThat(first.getId()).isEqualTo(captureSessionEntity.getId()); - assertThat(first.getStartTime()).isEqualTo(captureSessionEntity.getStartedAt()); - assertThat(first.getEndTime()).isEqualTo(captureSessionEntity.getFinishedAt()); + assertThat(first.getDate()).isEqualTo(DateTimeUtils.formatDate(captureSessionEntity.getStartedAt())); + assertThat(first.getStartTime()).isEqualTo(DateTimeUtils.formatTime(captureSessionEntity.getStartedAt())); + assertThat(first.getEndTime()).isNull(); assertThat(first.getDuration()).isNull(); assertThat(first.getCaseReference()).isEqualTo(caseEntity.getReference()); assertThat(first.getCourt()).isEqualTo(courtEntity.getName()); - assertThat(first.getRegion().stream().findFirst().isPresent()).isTrue(); - assertThat(first.getRegion().stream().findFirst().get().getName()).isEqualTo(regionEntity.getName()); + assertThat(first.getRegion()).isEqualTo(regionEntity.getName()); + assertThat(first.getCounty()).isEqualTo(courtEntity.getCounty()); + assertThat(first.getPostcode()).isEqualTo(courtEntity.getPostcode()); } @DisplayName("Find all capture sessions and return a list of models as a report on concurrent capture sessions") @@ -177,14 +180,15 @@ void captureSessionReportConcurrentCaptureSessionsSuccess() { assertThat(report.size()).isEqualTo(1); var first = report.getFirst(); - assertThat(first.getId()).isEqualTo(captureSessionEntity.getId()); - assertThat(first.getStartTime()).isEqualTo(captureSessionEntity.getStartedAt()); - assertThat(first.getEndTime()).isEqualTo(captureSessionEntity.getFinishedAt()); - assertThat(first.getDuration()).isEqualTo(recordingEntity.getDuration()); + assertThat(first.getDate()).isEqualTo(DateTimeUtils.formatDate(captureSessionEntity.getStartedAt())); + assertThat(first.getStartTime()).isEqualTo(DateTimeUtils.formatTime(captureSessionEntity.getStartedAt())); + assertThat(first.getEndTime()).isEqualTo(DateTimeUtils.formatTime(captureSessionEntity.getFinishedAt())); + assertThat(first.getDurationAsString()).isEqualTo("00:03:00"); assertThat(first.getCaseReference()).isEqualTo(caseEntity.getReference()); assertThat(first.getCourt()).isEqualTo(courtEntity.getName()); - assertThat(first.getRegion().stream().findFirst().isPresent()).isTrue(); - assertThat(first.getRegion().stream().findFirst().get().getName()).isEqualTo(regionEntity.getName()); + assertThat(first.getRegion()).isEqualTo(regionEntity.getName()); + assertThat(first.getCounty()).isEqualTo(courtEntity.getCounty()); + assertThat(first.getPostcode()).isEqualTo(courtEntity.getPostcode()); } @DisplayName("Find counts for recordings per case an return a report list") @@ -196,25 +200,18 @@ void reportRecordingsPerCaseSuccess() { anotherCase.setReference("XYZ456"); when(recordingRepository.countRecordingsPerCase()) - .thenReturn(List.of(new Object[] { caseEntity, (long) 1 }, new Object[]{ anotherCase, (long) 0 })); + .thenReturn(List.of(new Object[] { caseEntity, 1L }, new Object[]{ anotherCase, (long) 0 })); var report = reportService.reportRecordingsPerCase(); assertThat(report.size()).isEqualTo(2); assertThat(report.get(0).getCaseReference()).isEqualTo(caseEntity.getReference()); - assertThat(report.get(0).getCount()).isEqualTo(1); + assertThat(report.get(0).getNumberOfRecordings()).isEqualTo(1); assertThat(report.get(1).getCaseReference()).isEqualTo(anotherCase.getReference()); - assertThat(report.get(1).getCount()).isEqualTo(0); + assertThat(report.get(1).getNumberOfRecordings()).isEqualTo(0); assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); - assertThat(report - .getFirst() - .getRegions() - .stream() - .toList() - .getFirst() - .getName() - ).isEqualTo(regionEntity.getName()); + assertThat(report.getFirst().getRegion()).isEqualTo(regionEntity.getName()); } @DisplayName("Find all edited recordings and return a report list") @@ -233,21 +230,14 @@ void reportEditsSuccess() { assertThat(report.size()).isEqualTo(2); - assertThat(report.getFirst().getCreatedAt()).isEqualTo(recordingEntity.getCreatedAt()); + assertThat(report.getFirst().getEditDate()).isEqualTo(DateTimeUtils.formatDate(recordingEntity.getCreatedAt())); assertThat(report.getFirst().getVersion()).isEqualTo(recordingEntity.getVersion()); assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); - assertThat(report - .getFirst() - .getRegions() - .stream() - .toList() - .getFirst() - .getName() - ).isEqualTo(regionEntity.getName()); - assertThat(report.getFirst().getRecordingId()).isEqualTo(recordingEntity.getId()); - - assertThat(report.getLast().getRecordingId()).isEqualTo(recording2.getId()); + assertThat(report.getFirst().getRegion()).isEqualTo(regionEntity.getName()); + + assertThat(report.getFirst().getVersion()).isEqualTo(2); + assertThat(report.getLast().getVersion()).isEqualTo(3); } @DisplayName("Find shared bookings and return report list") @@ -273,29 +263,27 @@ void reportShared() { sharedBooking2.setSharedWith(shareWith); sharedBooking2.setSharedBy(shareBy); - when(shareBookingRepository.searchAll(null, null, null, null)) + when(shareBookingRepository.searchAll(null, null, null, null, false)) .thenReturn(List.of(sharedBooking1, sharedBooking2)); - var report = reportService.reportShared(null, null, null, null); + var report = reportService.reportShared(null, null, null, null, false); assertThat(report.size()).isEqualTo(2); - assertThat(report.getFirst().getSharedAt()).isEqualTo(sharedBooking2.getCreatedAt()); - assertThat(report.getFirst().getAllocatedTo()).isEqualTo(sharedBooking2.getSharedWith().getEmail()); - assertThat(report.getFirst().getAllocatedBy()).isEqualTo(sharedBooking2.getSharedBy().getEmail()); + assertThat(report.getFirst().getShareDate()).isEqualTo(DateTimeUtils.formatDate(sharedBooking2.getCreatedAt())); + assertThat(report.getFirst().getShareTime()).isEqualTo(DateTimeUtils.formatTime(sharedBooking2.getCreatedAt())); + assertThat(report.getFirst().getTimezone()) + .isEqualTo(DateTimeUtils.getTimezoneAbbreviation(sharedBooking2.getCreatedAt())); + assertThat(report.getFirst().getSharedWith()).isEqualTo(sharedBooking2.getSharedWith().getEmail()); + assertThat(report.getFirst().getGrantedBy()).isEqualTo(sharedBooking2.getSharedBy().getEmail()); assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); - assertThat(report - .getFirst() - .getRegions() - .stream() - .toList() - .getFirst() - .getName() - ).isEqualTo(regionEntity.getName()); - assertThat(report.getFirst().getBookingId()).isEqualTo(sharedBooking2.getBooking().getId()); - - assertThat(report.getLast().getSharedAt()).isEqualTo(sharedBooking1.getCreatedAt()); + assertThat(report.getFirst().getRegion()).isEqualTo(regionEntity.getName()); + + assertThat(report.getLast().getShareDate()).isEqualTo(DateTimeUtils.formatDate(sharedBooking1.getCreatedAt())); + assertThat(report.getLast().getShareTime()).isEqualTo(DateTimeUtils.formatTime(sharedBooking1.getCreatedAt())); + assertThat(report.getLast().getTimezone()) + .isEqualTo(DateTimeUtils.getTimezoneAbbreviation(sharedBooking1.getCreatedAt())); } @DisplayName("Find all capture sessions with recording available and get report on booking details") @@ -308,11 +296,13 @@ void reportScheduledSuccess() { captureSessionEntity.setStartedByUser(userEntity); var otherBooking = new Booking(); + otherBooking.setCreatedAt(Timestamp.from(Instant.now())); otherBooking.setId(UUID.randomUUID()); otherBooking.setCaseId(caseEntity); otherBooking.setScheduledFor(Timestamp.from(Instant.MIN)); captureSessionEntity.getBooking().setScheduledFor(Timestamp.from(Instant.MAX)); + bookingEntity.setCreatedAt(Timestamp.from(Instant.now())); var otherCaptureSessionEntity = new CaptureSession(); otherCaptureSessionEntity.setId(UUID.randomUUID()); @@ -326,12 +316,21 @@ void reportScheduledSuccess() { var report = reportService.reportScheduled(); assertThat(report.size()).isEqualTo(2); - assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); - assertThat(report.getFirst().getScheduledFor()).isEqualTo(captureSessionEntity.getBooking().getScheduledFor()); - assertThat(report.getFirst().getCaptureSessionUser()).isEqualTo(userEntity.getEmail()); - - assertThat(report.get(1).getCaseReference()).isEqualTo(caseEntity.getReference()); - assertThat(report.get(1).getScheduledFor()).isEqualTo(otherBooking.getScheduledFor()); + var first = report.getFirst(); + assertThat(first.getScheduledDate()) + .isEqualTo(DateTimeUtils.formatDate(captureSessionEntity.getBooking().getScheduledFor())); + assertThat(first.getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(first.getCourt()).isEqualTo(courtEntity.getName()); + assertThat(first.getCounty()).isEqualTo(courtEntity.getCounty()); + assertThat(first.getPostcode()).isEqualTo(courtEntity.getPostcode()); + assertThat(first.getRegion()).isEqualTo(regionEntity.getName()); + assertThat(first.getDateOfBooking()) + .isEqualTo(DateTimeUtils.formatDate(captureSessionEntity.getBooking().getCreatedAt())); + assertThat(first.getUser()).isEqualTo(userEntity.getEmail()); + + assertThat(report.getLast().getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(report.getLast().getScheduledDate()) + .isEqualTo(DateTimeUtils.formatDate(otherBooking.getScheduledFor())); } @DisplayName("Find audits relating to playbacks from the portal and return a report") @@ -363,20 +362,21 @@ void reportPlaybackPortalSuccess() { var report = reportService.reportPlayback(AuditLogSource.PORTAL); assertThat(report.size()).isEqualTo(1); - assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); - assertThat(report.getFirst().getUserEmail()).isEqualTo(user.getEmail()); + assertThat(report.getFirst().getPlaybackDate()).isEqualTo(DateTimeUtils.formatDate(auditEntity.getCreatedAt())); + assertThat(report.getFirst().getPlaybackTime()).isEqualTo(DateTimeUtils.formatTime(auditEntity.getCreatedAt())); + assertThat(report.getFirst().getPlaybackTimeZone()) + .isEqualTo(DateTimeUtils.getTimezoneAbbreviation(auditEntity.getCreatedAt())); + + assertThat(report.getFirst().getUserFullName()).isEqualTo(user.getFullName()); + assertThat(report.getFirst().getUserEmail()).isEqualTo(user.getEmail()); + assertThat(report.getFirst().getUserOrganisation()).isEqualTo(user.getOrganisation()); assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); - assertThat(report.getFirst().getRecordingId()).isEqualTo(recordingEntity.getId()); - assertThat(report - .getFirst() - .getRegions() - .stream() - .toList() - .getFirst() - .getName() - ).isEqualTo(regionEntity.getName()); + assertThat(report.getFirst().getCounty()).isEqualTo(courtEntity.getCounty()); + assertThat(report.getFirst().getPostcode()).isEqualTo(courtEntity.getPostcode()); + + assertThat(report.getFirst().getRegion()).isEqualTo(regionEntity.getName()); } @DisplayName("Find audits relating to playbacks from the portal and return a report using erroneous recordinguid") @@ -401,20 +401,15 @@ void reportPlaybackPortalSuccessRecordingid() { var report = reportService.reportPlayback(AuditLogSource.PORTAL); assertThat(report.size()).isEqualTo(1); - assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); - assertThat(report.getFirst().getUserEmail()).isNullOrEmpty(); - assertThat(report.getFirst().getUserFullName()).isNullOrEmpty(); + assertThat(report.getFirst().getPlaybackDate()).isEqualTo(DateTimeUtils.formatDate(auditEntity.getCreatedAt())); + assertThat(report.getFirst().getPlaybackTime()).isEqualTo(DateTimeUtils.formatTime(auditEntity.getCreatedAt())); + assertThat(report.getFirst().getPlaybackTimeZone()) + .isEqualTo(DateTimeUtils.getTimezoneAbbreviation(auditEntity.getCreatedAt())); assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); - assertThat(report.getFirst().getRecordingId()).isEqualTo(recordingEntity.getId()); - assertThat(report - .getFirst() - .getRegions() - .stream() - .toList() - .getFirst() - .getName() - ).isEqualTo(regionEntity.getName()); + assertThat(report.getFirst().getCounty()).isEqualTo(courtEntity.getCounty()); + assertThat(report.getFirst().getPostcode()).isEqualTo(courtEntity.getPostcode()); + assertThat(report.getFirst().getRegion()).isEqualTo(regionEntity.getName()); } @DisplayName("Find audits relating to playbacks from the portal and return a report without recordingid") @@ -438,12 +433,15 @@ void reportPlaybackPortalSuccessNoRecordingId() { var report = reportService.reportPlayback(AuditLogSource.PORTAL); assertThat(report.size()).isEqualTo(1); - assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); - assertThat(report.getFirst().getUserEmail()).isNullOrEmpty(); - assertThat(report.getFirst().getUserFullName()).isNullOrEmpty(); + assertThat(report.getFirst().getPlaybackDate()).isEqualTo(DateTimeUtils.formatDate(auditEntity.getCreatedAt())); + assertThat(report.getFirst().getPlaybackTime()).isEqualTo(DateTimeUtils.formatTime(auditEntity.getCreatedAt())); + assertThat(report.getFirst().getPlaybackTimeZone()) + .isEqualTo(DateTimeUtils.getTimezoneAbbreviation(auditEntity.getCreatedAt())); assertThat(report.getFirst().getCaseReference()).isNullOrEmpty(); assertThat(report.getFirst().getCourt()).isNullOrEmpty(); - assertThat(report.getFirst().getRecordingId()).isNull(); + assertThat(report.getFirst().getCounty()).isNullOrEmpty(); + assertThat(report.getFirst().getPostcode()).isNullOrEmpty(); + assertThat(report.getFirst().getRegion()).isNullOrEmpty(); } @DisplayName("Find audits relating to playbacks from the portal and return a report without audit details") @@ -461,12 +459,19 @@ void reportPlaybackPortalSuccessNoAuditDetails() { var report = reportService.reportPlayback(AuditLogSource.PORTAL); assertThat(report.size()).isEqualTo(1); - assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); - assertThat(report.getFirst().getUserEmail()).isNullOrEmpty(); + + assertThat(report.getFirst().getPlaybackDate()).isEqualTo(DateTimeUtils.formatDate(auditEntity.getCreatedAt())); + assertThat(report.getFirst().getPlaybackTime()).isEqualTo(DateTimeUtils.formatTime(auditEntity.getCreatedAt())); + assertThat(report.getFirst().getPlaybackTimeZone()) + .isEqualTo(DateTimeUtils.getTimezoneAbbreviation(auditEntity.getCreatedAt())); assertThat(report.getFirst().getUserFullName()).isNullOrEmpty(); - assertThat(report.getFirst().getCaseReference()).isNullOrEmpty(); // has a value??? + assertThat(report.getFirst().getUserEmail()).isNullOrEmpty(); + assertThat(report.getFirst().getUserOrganisation()).isNullOrEmpty(); + assertThat(report.getFirst().getCaseReference()).isNullOrEmpty(); assertThat(report.getFirst().getCourt()).isNullOrEmpty(); - assertThat(report.getFirst().getRecordingId()).isNull(); + assertThat(report.getFirst().getCounty()).isNullOrEmpty(); + assertThat(report.getFirst().getPostcode()).isNullOrEmpty(); + assertThat(report.getFirst().getRegion()).isNullOrEmpty(); } @DisplayName("Find audits relating to playbacks from the application and return a report") @@ -497,21 +502,20 @@ void reportPlaybackApplicationSuccess() { var report = reportService.reportPlayback(AuditLogSource.APPLICATION); - assertThat(report.size()).isEqualTo(1); - assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); - assertThat(report.getFirst().getUserEmail()).isEqualTo(user.getEmail()); + assertThat(report.getFirst().getPlaybackDate()).isEqualTo(DateTimeUtils.formatDate(auditEntity.getCreatedAt())); + assertThat(report.getFirst().getPlaybackTime()).isEqualTo(DateTimeUtils.formatTime(auditEntity.getCreatedAt())); + assertThat(report.getFirst().getPlaybackTimeZone()) + .isEqualTo(DateTimeUtils.getTimezoneAbbreviation(auditEntity.getCreatedAt())); + + assertThat(report.getFirst().getUserFullName()).isEqualTo(user.getFullName()); + assertThat(report.getFirst().getUserOrganisation()).isEqualTo(user.getOrganisation()); assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); - assertThat(report.getFirst().getRecordingId()).isEqualTo(recordingEntity.getId()); - assertThat(report - .getFirst() - .getRegions() - .stream() - .toList() - .getFirst() - .getName() - ).isEqualTo(regionEntity.getName()); + assertThat(report.getFirst().getCounty()).isEqualTo(courtEntity.getCounty()); + assertThat(report.getFirst().getPostcode()).isEqualTo(courtEntity.getPostcode()); + + assertThat(report.getFirst().getRegion()).isEqualTo(regionEntity.getName()); } @DisplayName("Find audits relating to all playback attempts and return a report") @@ -535,21 +539,21 @@ void reportPlaybackAllSuccess() { var report = reportService.reportPlayback(null); - assertThat(report.size()).isEqualTo(1); - assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); - assertThat(report.getFirst().getUserEmail()).isEqualTo(user.getEmail()); + assertThat(report.getFirst().getPlaybackDate()).isEqualTo(DateTimeUtils.formatDate(auditEntity.getCreatedAt())); + assertThat(report.getFirst().getPlaybackTime()).isEqualTo(DateTimeUtils.formatTime(auditEntity.getCreatedAt())); + assertThat(report.getFirst().getPlaybackTimeZone()) + .isEqualTo(DateTimeUtils.getTimezoneAbbreviation(auditEntity.getCreatedAt())); + + assertThat(report.getFirst().getUserFullName()).isEqualTo(user.getFullName()); + assertThat(report.getFirst().getUserEmail()).isEqualTo(user.getEmail()); + assertThat(report.getFirst().getUserOrganisation()).isEqualTo(user.getOrganisation()); assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); - assertThat(report.getFirst().getRecordingId()).isEqualTo(recordingEntity.getId()); - assertThat(report - .getFirst() - .getRegions() - .stream() - .toList() - .getFirst() - .getName() - ).isEqualTo(regionEntity.getName()); + assertThat(report.getFirst().getCounty()).isEqualTo(courtEntity.getCounty()); + assertThat(report.getFirst().getPostcode()).isEqualTo(courtEntity.getPostcode()); + + assertThat(report.getFirst().getRegion()).isEqualTo(regionEntity.getName()); verify(auditRepository, times(1)).findAllAccessAttempts(); verify(auditRepository, never()).findBySourceAndFunctionalAreaAndActivity(any(), any(), any()); @@ -573,44 +577,19 @@ void reportPlaybackAllSuccessNullAuditDetails() { var report = reportService.reportPlayback(null); assertThat(report.size()).isEqualTo(1); - assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); - assertThat(report.getFirst().getUserEmail()).isEqualTo(user.getEmail()); + assertThat(report.getFirst().getPlaybackDate()).isEqualTo(DateTimeUtils.formatDate(auditEntity.getCreatedAt())); + assertThat(report.getFirst().getPlaybackTime()).isEqualTo(DateTimeUtils.formatTime(auditEntity.getCreatedAt())); + assertThat(report.getFirst().getPlaybackTimeZone()) + .isEqualTo(DateTimeUtils.getTimezoneAbbreviation(auditEntity.getCreatedAt())); assertThat(report.getFirst().getUserFullName()).isEqualTo(user.getFullName()); - assertThat(report.getFirst().getCaseReference()).isEqualTo(null); - assertThat(report.getFirst().getCourt()).isEqualTo(null); - assertThat(report.getFirst().getRecordingId()).isEqualTo(null); - - verify(auditRepository, times(1)).findAllAccessAttempts(); - verify(auditRepository, never()).findBySourceAndFunctionalAreaAndActivity(any(), any(), any()); - } - - @Test - @DisplayName("Returns audits for all playback attempts a report when createdBy is appAccess id not of user id") - void reportPlaybackAllSuccessAuditDetailsWhenAppAccessId() { - var user = new User(); - user.setId(UUID.randomUUID()); - user.setEmail("example@example.com"); - user.setFirstName("Example"); - user.setLastName("Person"); - var appAccess = new AppAccess(); - appAccess.setId(UUID.randomUUID()); - appAccess.setUser(user); - auditEntity.setCreatedBy(appAccess.getId()); - auditEntity.setSource(AuditLogSource.APPLICATION); - - when(auditRepository.findAllAccessAttempts()).thenReturn(List.of(auditEntity)); - when(userRepository.findById(user.getId())).thenReturn(Optional.empty()); - when(appAccessRepository.findById(appAccess.getId())).thenReturn(Optional.of(appAccess)); - when(recordingRepository.findById(recordingEntity.getId())).thenReturn(Optional.of(recordingEntity)); - - var report = reportService.reportPlayback(null); - - assertThat(report.size()).isEqualTo(1); - assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); assertThat(report.getFirst().getUserEmail()).isEqualTo(user.getEmail()); - assertThat(report.getFirst().getUserFullName()).isEqualTo(user.getFullName()); + assertThat(report.getFirst().getUserOrganisation()).isEqualTo(user.getOrganisation()); + assertThat(report.getFirst().getCaseReference()).isEqualTo(null); + assertThat(report.getFirst().getCourt()).isNullOrEmpty(); + assertThat(report.getFirst().getCounty()).isNullOrEmpty(); + assertThat(report.getFirst().getPostcode()).isNullOrEmpty(); + assertThat(report.getFirst().getRegion()).isNullOrEmpty(); - verify(appAccessRepository, times(1)).findById(appAccess.getId()); verify(auditRepository, times(1)).findAllAccessAttempts(); verify(auditRepository, never()).findBySourceAndFunctionalAreaAndActivity(any(), any(), any()); } @@ -638,8 +617,7 @@ void reportPlaybackAllSuccessAuditDetailsWhenPortalAccessId() { var report = reportService.reportPlayback(null); assertThat(report.size()).isEqualTo(1); - assertThat(report.getFirst().getPlaybackAt()).isEqualTo(auditEntity.getCreatedAt()); - assertThat(report.getFirst().getUserEmail()).isEqualTo(user.getEmail()); + assertThat(report.getFirst().getPlaybackDate()).isEqualTo(DateTimeUtils.formatDate(auditEntity.getCreatedAt())); assertThat(report.getFirst().getUserFullName()).isEqualTo(user.getFullName()); verify(appAccessRepository, times(1)).findById(portalAccess.getId()); @@ -689,28 +667,29 @@ void reportCompletedCaptureSessionsSuccess() { defendant.setCaseId(caseEntity); bookingEntity.setParticipants(Set.of(witness, defendant)); + bookingEntity.setScheduledFor(Timestamp.from(Instant.now())); when(recordingRepository.findAllByParentRecordingIsNull()).thenReturn(List.of(recordingEntity)); var report = reportService.reportCompletedCaptureSessions(); - assertThat(report.getFirst().getStartedAt()).isEqualTo(captureSessionEntity.getStartedAt()); - assertThat(report.getFirst().getFinishedAt()).isEqualTo(captureSessionEntity.getFinishedAt()); - assertThat(report.getFirst().getDuration()).isEqualTo(recordingEntity.getDuration()); - assertThat(report.getFirst().getScheduledFor()).isEqualTo(bookingEntity.getScheduledFor()); - assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); - assertThat(report.getFirst().getCountDefendants()).isEqualTo(1); - assertThat(report.getFirst().getCountWitnesses()).isEqualTo(1); - assertThat(report.getFirst().getRecordingStatus()).isEqualTo(captureSessionEntity.getStatus()); - assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); - assertThat(report - .getFirst() - .getRegions() - .stream() - .toList() - .getFirst() - .getName() - ).isEqualTo(regionEntity.getName()); + var first = report.getFirst(); + assertThat(first.getRecordingDate()).isEqualTo(DateTimeUtils.formatDate(captureSessionEntity.getStartedAt())); + assertThat(first.getRecordingTime()).isEqualTo(DateTimeUtils.formatTime(captureSessionEntity.getStartedAt())); + assertThat(first.getFinishTime()).isEqualTo(DateTimeUtils.formatTime(captureSessionEntity.getFinishedAt())); + assertThat(first.getTimezone()) + .isEqualTo(DateTimeUtils.getTimezoneAbbreviation(captureSessionEntity.getStartedAt())); + assertThat(first.getScheduledDate()).isEqualTo(DateTimeUtils.formatDate(bookingEntity.getScheduledFor())); + assertThat(first.getCaseReference()).isEqualTo(caseEntity.getReference()); + assertThat(first.getStatus()).isEqualTo(captureSessionEntity.getStatus()); + assertThat(first.getDefendantNames()).isEqualTo(defendant.getFullName()); + assertThat(first.getDefendant()).isEqualTo(1); + assertThat(first.getWitnessNames()).isEqualTo(witness.getFullName()); + assertThat(first.getWitness()).isEqualTo(1); + assertThat(first.getCourt()).isEqualTo(courtEntity.getName()); + assertThat(first.getCounty()).isEqualTo(courtEntity.getCounty()); + assertThat(first.getPostcode()).isEqualTo(courtEntity.getPostcode()); + assertThat(first.getRegion()).isEqualTo(regionEntity.getName()); } @DisplayName("Find all share booking removals and return a report") @@ -731,20 +710,12 @@ void reportAccessRemovedSuccess() { var report = reportService.reportAccessRemoved(); - assertThat(report.getFirst().getRemovedAt()).isEqualTo(shareBooking.getDeletedAt()); + assertThat(report.getFirst().getRemovedDate()).isEqualTo(DateTimeUtils.formatDate(shareBooking.getDeletedAt())); assertThat(report.getFirst().getCaseReference()).isEqualTo(caseEntity.getReference()); assertThat(report.getFirst().getCourt()).isEqualTo(courtEntity.getName()); - assertThat(report - .getFirst() - .getRegions() - .stream() - .toList() - .getFirst() - .getName() - ).isEqualTo(regionEntity.getName()); - assertThat(report.getFirst().getUserFullName()).isEqualTo(user.getFirstName() + " " + user.getLastName()); + assertThat(report.getFirst().getRegion()).isEqualTo(regionEntity.getName()); + assertThat(report.getFirst().getFullName()).isEqualTo(user.getFirstName() + " " + user.getLastName()); assertThat(report.getFirst().getUserEmail()).isEqualTo(user.getEmail()); - assertThat(report.getFirst().getRemovalReason()).isNull(); } @DisplayName("Find all participants and the recordings they are linked to") diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/util/HelperFactory.java b/src/test/java/uk/gov/hmcts/reform/preapi/util/HelperFactory.java index 0f63d8ec9..47e0cfe6d 100644 --- a/src/test/java/uk/gov/hmcts/reform/preapi/util/HelperFactory.java +++ b/src/test/java/uk/gov/hmcts/reform/preapi/util/HelperFactory.java @@ -157,6 +157,7 @@ public static CaptureSession createCaptureSession(//NOPMD - suppressed Excessive captureSession.setFinishedByUser(finishedBy); captureSession.setStatus(status); captureSession.setDeletedAt(deletedAt); + captureSession.setRecordings(Set.of()); return captureSession; } diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/utils/DateTimeUtilsTest.java b/src/test/java/uk/gov/hmcts/reform/preapi/utils/DateTimeUtilsTest.java new file mode 100644 index 000000000..e23c35896 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/preapi/utils/DateTimeUtilsTest.java @@ -0,0 +1,58 @@ +package uk.gov.hmcts.reform.preapi.utils; + +import org.junit.jupiter.api.Test; + +import java.sql.Timestamp; +import java.time.Instant; +import java.time.ZonedDateTime; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class DateTimeUtilsTest { + + @Test + public void formatDateSuccess() { + var timestamp = Timestamp.from(Instant.now()); + var result = DateTimeUtils.formatDate(timestamp); + + assertThat(result).isNotNull(); + assertThat(result).matches("^\\d{2}/\\d{2}/\\d{4}$"); + } + + @Test + public void formatDateNull() { + assertThrows(IllegalArgumentException.class, () -> DateTimeUtils.formatDate(null)); + } + + @Test + public void formatTimeSuccess() { + var timestamp = Timestamp.from(Instant.now()); + var result = DateTimeUtils.formatTime(timestamp); + + assertThat(result).isNotNull(); + assertThat(result).matches("^\\d{2}:\\d{2}:\\d{2}$"); + } + + @Test + public void formatTimeNull() { + assertThrows(IllegalArgumentException.class, () -> DateTimeUtils.formatTime(null)); + } + + @Test + public void getTimezoneAbbreviationBST() { + var timestamp = Timestamp.from(ZonedDateTime.of(2025, 7, 15, 0, 0, 0, 0, DateTimeUtils.TIME_ZONE).toInstant()); + assertThat(DateTimeUtils.getTimezoneAbbreviation(timestamp)).isEqualTo("BST"); + } + + @Test + public void getTimezoneAbbreviationGMT() { + var timestamp = Timestamp.from(ZonedDateTime.of(2025, 12, 15, 0, 0, 0, 0, DateTimeUtils.TIME_ZONE).toInstant()); + assertThat(DateTimeUtils.getTimezoneAbbreviation(timestamp)).isEqualTo("GMT"); + } + + @Test + public void getTimezoneAbbreviationNull() { + assertThrows(IllegalArgumentException.class, () -> DateTimeUtils.getTimezoneAbbreviation(null)); + } +}