From 8d1146d734d587b23a1e4c215af26b8963feedd0 Mon Sep 17 00:00:00 2001 From: Marcus Aspin Date: Thu, 19 Oct 2023 17:21:44 +0000 Subject: [PATCH] PI-1505 DPS probation document endpoints --- projects/dps-and-delius/.trivyignore | 3 + projects/dps-and-delius/build.gradle.kts | 1 + .../justice/digital/hmpps/data/DataLoader.kt | 43 ++++- .../digital/hmpps/data/entity/Entities.kt | 86 +++++++++ .../hmpps/data/generator/DocumentGenerator.kt | 35 ++++ .../hmpps/data/generator/EventGenerator.kt | 100 ++++++++++ .../hmpps/data/generator/PersonGenerator.kt | 17 ++ .../src/dev/resources/local-public-key.pub | 8 +- .../simulations/__files/document.pdf | Bin 0 -> 14812 bytes .../__files/hmpps-auth-token-body.json | 2 +- .../simulations/mappings/alfresco.json | 15 ++ .../justice/digital/hmpps/IntegrationTest.kt | 51 ++++- .../digital/hmpps/client/AlfrescoClient.kt | 14 ++ .../digital/hmpps/config/FeignConfig.kt | 23 +++ .../digital/hmpps/controller/ApiController.kt | 17 -- .../hmpps/controller/DocumentController.kt | 33 ++++ .../digital/hmpps/entity/CourtAppearance.kt | 36 ++++ .../justice/digital/hmpps/entity/Document.kt | 175 ++++++++++++++++++ .../gov/justice/digital/hmpps/entity/Event.kt | 122 ++++++++++++ .../justice/digital/hmpps/entity/Offence.kt | 43 +++++ .../justice/digital/hmpps/entity/Person.kt | 49 +++++ .../digital/hmpps/entity/ReferenceData.kt | 22 +++ .../justice/digital/hmpps/model/Conviction.kt | 12 ++ .../justice/digital/hmpps/model/Document.kt | 12 ++ .../gov/justice/digital/hmpps/model/Name.kt | 7 + .../hmpps/model/ProbationDocumentsResponse.kt | 8 + .../digital/hmpps/service/DocumentService.kt | 95 ++++++++++ .../src/main/resources/application.yml | 10 + 28 files changed, 1013 insertions(+), 26 deletions(-) create mode 100644 projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/entity/Entities.kt create mode 100644 projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/DocumentGenerator.kt create mode 100644 projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/EventGenerator.kt create mode 100644 projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt create mode 100644 projects/dps-and-delius/src/dev/resources/simulations/__files/document.pdf create mode 100644 projects/dps-and-delius/src/dev/resources/simulations/mappings/alfresco.json create mode 100644 projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/client/AlfrescoClient.kt create mode 100644 projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/config/FeignConfig.kt delete mode 100644 projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/controller/ApiController.kt create mode 100644 projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/controller/DocumentController.kt create mode 100644 projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/CourtAppearance.kt create mode 100644 projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/Document.kt create mode 100644 projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/Event.kt create mode 100644 projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/Offence.kt create mode 100644 projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/Person.kt create mode 100644 projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/ReferenceData.kt create mode 100644 projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/model/Conviction.kt create mode 100644 projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/model/Document.kt create mode 100644 projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/model/Name.kt create mode 100644 projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/model/ProbationDocumentsResponse.kt create mode 100644 projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/DocumentService.kt diff --git a/projects/dps-and-delius/.trivyignore b/projects/dps-and-delius/.trivyignore index e69de29bb2..c2e127ba68 100644 --- a/projects/dps-and-delius/.trivyignore +++ b/projects/dps-and-delius/.trivyignore @@ -0,0 +1,3 @@ +# Suppressed as we do not process any untrusted YML content +# Note: this will be resolved in Spring Boot 3.2: https://github.com/spring-projects/spring-boot/issues/35982 +CVE-2022-1471 exp:2023-12-01 \ No newline at end of file diff --git a/projects/dps-and-delius/build.gradle.kts b/projects/dps-and-delius/build.gradle.kts index 687f79a7a0..f88cb5a1cf 100644 --- a/projects/dps-and-delius/build.gradle.kts +++ b/projects/dps-and-delius/build.gradle.kts @@ -16,6 +16,7 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation(libs.openfeign) implementation(libs.springdoc) dev(project(":libs:dev-tools")) diff --git a/projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt b/projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt index f6ea94c9a7..5cd20517a1 100644 --- a/projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt +++ b/projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt @@ -1,17 +1,23 @@ package uk.gov.justice.digital.hmpps.data import jakarta.annotation.PostConstruct +import jakarta.persistence.EntityManager +import jakarta.transaction.Transactional import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.boot.context.event.ApplicationReadyEvent import org.springframework.context.ApplicationListener import org.springframework.stereotype.Component +import uk.gov.justice.digital.hmpps.data.generator.DocumentGenerator +import uk.gov.justice.digital.hmpps.data.generator.EventGenerator +import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator import uk.gov.justice.digital.hmpps.data.generator.UserGenerator import uk.gov.justice.digital.hmpps.user.AuditUserRepository @Component @ConditionalOnProperty("seed.database") class DataLoader( - private val auditUserRepository: AuditUserRepository + private val auditUserRepository: AuditUserRepository, + private val entityManager: EntityManager ) : ApplicationListener { @PostConstruct @@ -19,7 +25,40 @@ class DataLoader( auditUserRepository.save(UserGenerator.AUDIT_USER) } + @Transactional override fun onApplicationEvent(are: ApplicationReadyEvent) { - // Perform dev/test database setup here, using JPA repositories and generator classes... + entityManager.persist(PersonGenerator.DEFAULT) + entityManager.persist(EventGenerator.EVENT.mainOffence.offence) + entityManager.persist(EventGenerator.EVENT) + entityManager.persist(EventGenerator.EVENT.mainOffence) + entityManager.persist(EventGenerator.DISPOSAL.lengthUnits) + entityManager.persist(EventGenerator.DISPOSAL.type) + entityManager.persist(EventGenerator.DISPOSAL) + entityManager.persist(EventGenerator.INSTITUTION) + entityManager.persist(EventGenerator.CUSTODY) + entityManager.persist(EventGenerator.COURT) + entityManager.persist(EventGenerator.COURT_APPEARANCE.outcome) + entityManager.persist(EventGenerator.COURT_APPEARANCE) + entityManager.persist(EventGenerator.COURT_REPORT_TYPE) + entityManager.persist(EventGenerator.COURT_REPORT) + entityManager.persist(EventGenerator.INSTITUTIONAL_REPORT_TYPE) + entityManager.persist(EventGenerator.INSTITUTIONAL_REPORT) + entityManager.persist(EventGenerator.CONTACT_TYPE) + entityManager.persist(EventGenerator.CONTACT) + entityManager.persist(EventGenerator.NSI_TYPE) + entityManager.persist(EventGenerator.NSI) + entityManager.persist(DocumentGenerator.OFFENDER) + entityManager.persist(DocumentGenerator.PREVIOUS_CONVICTIONS) + entityManager.persist(DocumentGenerator.EVENT) + entityManager.persist(DocumentGenerator.CPS_PACK) + entityManager.persist(DocumentGenerator.ADDRESSASSESSMENT) + entityManager.persist(DocumentGenerator.PERSONALCONTACT) + entityManager.persist(DocumentGenerator.PERSONAL_CIRCUMSTANCE) + entityManager.persist(DocumentGenerator.COURT_REPORT) + entityManager.persist(DocumentGenerator.INSTITUTIONAL_REPORT) + entityManager.persist(DocumentGenerator.OFFENDER_CONTACT) + entityManager.persist(DocumentGenerator.EVENT_CONTACT) + entityManager.persist(DocumentGenerator.OFFENDER_NSI) + entityManager.persist(DocumentGenerator.EVENT_NSI) } } diff --git a/projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/entity/Entities.kt b/projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/entity/Entities.kt new file mode 100644 index 0000000000..5ce6ecb916 --- /dev/null +++ b/projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/entity/Entities.kt @@ -0,0 +1,86 @@ +package uk.gov.justice.digital.hmpps.data.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.Id +import jakarta.persistence.Table +import java.time.LocalDate + +@Entity +data class AddressAssessment(@Id val addressAssessmentId: Long, val assessmentDate: LocalDate) + +@Entity +data class ApprovedPremisesReferral(@Id val approvedPremisesReferralId: Long, val referralDate: LocalDate, val eventId: Long?) + +@Entity +data class Assessment(@Id val assessmentId: Long, val assessmentTypeId: Long, val referralId: Long?, val assessmentDate: LocalDate) + +@Entity +@Table(name = "r_assessment_type") +data class AssessmentType(@Id val assessmentTypeId: Long, val description: String) + +@Entity +data class CaseAllocation(@Id val caseAllocationId: Long, val eventId: Long) + +@Entity +data class Contact(@Id val contactId: Long, val contactTypeId: Long, val eventId: Long?, val contactDate: LocalDate) + +@Entity +@Table(name = "r_contact_type") +data class ContactType(@Id val contactTypeId: Long, val description: String) + +@Entity +data class Court(@Id val courtId: Long, val courtName: String) + +@Entity +@Table(name = "r_court_report_type") +data class CourtReportType(@Id val courtReportTypeId: Long, val description: String) + +@Entity +data class CourtReport(@Id val courtReportId: Long, val courtReportTypeId: Long, val courtAppearanceId: Long, val dateRequested: LocalDate) + +@Entity +data class InstitutionalReport(@Id val institutionalReportId: Long, val institutionReportTypeId: Long, val institutionId: Long, val establishment: String, val custodyId: Long, val dateRequested: LocalDate) + +@Entity +data class Nsi(@Id val nsiId: Long, val nsiTypeId: Long, val eventId: Long?, val referralDate: LocalDate) + +@Entity +@Table(name = "r_nsi_type") +data class NsiType(@Id val nsiTypeId: Long, val description: String) + +@Entity +data class PersonalCircumstance(@Id val personalCircumstanceId: Long, val circumstanceTypeId: Long, val startDate: LocalDate) + +@Entity +@Table(name = "r_circumstance_type") +data class PersonalCircumstanceType(@Id val circumstanceTypeId: Long, val codeDescription: String) + +@Entity +data class PersonalContact(@Id val personalContactId: Long, val relationshipTypeId: Long, val relationship: String) + +@Entity +data class Referral(@Id val referralId: Long, val referralTypeId: Long, val referralDate: LocalDate, val eventId: Long) + +@Entity +@Table(name = "r_referral_type") +data class ReferralType(@Id val referralTypeId: Long, val description: String) + +@Entity +data class UpwAppointment(@Id val upwAppointmentId: Long, val upwDetailsId: Long, val upwProjectId: Long, val appointmentDate: LocalDate) + +@Entity +data class UpwDetails(@Id val upwDetailsId: Long, val disposalId: Long) + +@Entity +data class UpwProject(@Id val upwProjectId: Long, val disposalId: Long, val name: String) + +@Entity +@Table(name = "user_") +data class User( + @Id + @Column(name = "user_id") + val userId: Long, + val forename: String, + val surname: String +) diff --git a/projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/DocumentGenerator.kt b/projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/DocumentGenerator.kt new file mode 100644 index 0000000000..6f794785c8 --- /dev/null +++ b/projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/DocumentGenerator.kt @@ -0,0 +1,35 @@ +package uk.gov.justice.digital.hmpps.data.generator + +import uk.gov.justice.digital.hmpps.entity.Document +import java.time.ZonedDateTime +import java.util.UUID + +object DocumentGenerator { + val OFFENDER = generate("OFFENDER", primaryKeyId = PersonGenerator.DEFAULT.id, alfrescoId = "uuid1") + val PREVIOUS_CONVICTIONS = generate("OFFENDER", "PREVIOUS_CONVICTION", primaryKeyId = PersonGenerator.DEFAULT.id) + val EVENT = generate("EVENT", primaryKeyId = EventGenerator.EVENT.id) + val CPS_PACK = generate("EVENT", "CPS_PACK", primaryKeyId = EventGenerator.EVENT.id) + val ADDRESSASSESSMENT = generate("ADDRESSASSESSMENT") + val PERSONALCONTACT = generate("PERSONALCONTACT") + val PERSONAL_CIRCUMSTANCE = generate("PERSONAL_CIRCUMSTANCE") + val COURT_REPORT = generate("COURT_REPORT", primaryKeyId = EventGenerator.COURT_REPORT.courtReportId) + val INSTITUTIONAL_REPORT = generate("INSTITUTIONAL_REPORT", primaryKeyId = EventGenerator.INSTITUTIONAL_REPORT.institutionalReportId) + val OFFENDER_CONTACT = generate("CONTACT") + val EVENT_CONTACT = generate("CONTACT", primaryKeyId = EventGenerator.CONTACT.contactId) + val OFFENDER_NSI = generate("NSI") + val EVENT_NSI = generate("NSI", primaryKeyId = EventGenerator.NSI.nsiId) + + fun generate(tableName: String, type: String = "DOCUMENT", primaryKeyId: Long = 0, alfrescoId: String = UUID.randomUUID().toString()) = Document( + id = IdGenerator.getAndIncrement(), + personId = PersonGenerator.DEFAULT.id, + alfrescoId = alfrescoId, + primaryKeyId = primaryKeyId, + name = "$tableName-related document", + type = type, + tableName = tableName, + createdAt = ZonedDateTime.now(), + createdByUserId = 0, + lastUpdatedUserId = 0, + softDeleted = false + ) +} diff --git a/projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/EventGenerator.kt b/projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/EventGenerator.kt new file mode 100644 index 0000000000..e7bc0450fd --- /dev/null +++ b/projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/EventGenerator.kt @@ -0,0 +1,100 @@ +package uk.gov.justice.digital.hmpps.data.generator + +import uk.gov.justice.digital.hmpps.data.entity.Contact +import uk.gov.justice.digital.hmpps.data.entity.ContactType +import uk.gov.justice.digital.hmpps.data.entity.Court +import uk.gov.justice.digital.hmpps.data.entity.CourtReport +import uk.gov.justice.digital.hmpps.data.entity.CourtReportType +import uk.gov.justice.digital.hmpps.data.entity.InstitutionalReport +import uk.gov.justice.digital.hmpps.data.entity.Nsi +import uk.gov.justice.digital.hmpps.data.entity.NsiType +import uk.gov.justice.digital.hmpps.entity.CourtAppearance +import uk.gov.justice.digital.hmpps.entity.Custody +import uk.gov.justice.digital.hmpps.entity.Disposal +import uk.gov.justice.digital.hmpps.entity.DisposalType +import uk.gov.justice.digital.hmpps.entity.Event +import uk.gov.justice.digital.hmpps.entity.Institution +import uk.gov.justice.digital.hmpps.entity.MainOffence +import uk.gov.justice.digital.hmpps.entity.Offence +import uk.gov.justice.digital.hmpps.entity.ReferenceData +import uk.gov.justice.digital.hmpps.set +import java.time.LocalDate +import java.time.ZonedDateTime + +object EventGenerator { + val EVENT = Event( + id = IdGenerator.getAndIncrement(), + person = PersonGenerator.DEFAULT, + referralDate = LocalDate.now(), + mainOffence = MainOffence( + id = IdGenerator.getAndIncrement(), + offence = Offence( + id = IdGenerator.getAndIncrement(), + subCategoryDescription = "Burglary" + ) + ), + courtAppearances = listOf() + ).also { it.mainOffence.set("event", it) } + val DISPOSAL = Disposal( + id = IdGenerator.getAndIncrement(), + event = EVENT, + type = DisposalType( + id = IdGenerator.getAndIncrement(), + description = "Sentenced" + ), + length = 6, + lengthUnits = ReferenceData(IdGenerator.getAndIncrement(), "M", "Months") + ) + val INSTITUTION = Institution( + id = IdGenerator.getAndIncrement(), + name = "test institution", + establishment = "Y" + ) + val CUSTODY = Custody( + id = IdGenerator.getAndIncrement(), + disposal = DISPOSAL, + institution = INSTITUTION + ) + + val COURT = Court(courtId = IdGenerator.getAndIncrement(), courtName = "test court") + val COURT_REPORT_TYPE = CourtReportType(courtReportTypeId = IdGenerator.getAndIncrement(), description = "court report type") + val COURT_APPEARANCE = CourtAppearance( + id = IdGenerator.getAndIncrement(), + date = ZonedDateTime.now(), + courtId = COURT.courtId, + event = EVENT, + outcome = ReferenceData(IdGenerator.getAndIncrement(), "TEST", "Community Order") + ) + val COURT_REPORT = CourtReport( + courtReportId = IdGenerator.getAndIncrement(), + courtReportTypeId = COURT_REPORT_TYPE.courtReportTypeId, + courtAppearanceId = COURT_APPEARANCE.id, + dateRequested = LocalDate.of(2000, 1, 1) + ) + + val INSTITUTIONAL_REPORT_TYPE = ReferenceData(IdGenerator.getAndIncrement(), "IR", "institutional report type") + val INSTITUTIONAL_REPORT = InstitutionalReport( + institutionalReportId = IdGenerator.getAndIncrement(), + institutionId = INSTITUTION.id, + institutionReportTypeId = INSTITUTIONAL_REPORT_TYPE.id, + custodyId = CUSTODY.id, + establishment = "Y", + dateRequested = LocalDate.of(2000, 1, 2) + ) + + val CONTACT_TYPE = ContactType(contactTypeId = IdGenerator.getAndIncrement(), description = "contact type") + val CONTACT = Contact( + contactId = IdGenerator.getAndIncrement(), + contactTypeId = CONTACT_TYPE.contactTypeId, + eventId = EVENT.id, + contactDate = LocalDate.of(2000, 1, 3) + ) + + val NSI_TYPE = NsiType(nsiTypeId = IdGenerator.getAndIncrement(), description = "nsi type") + val NSI = Nsi( + nsiId = IdGenerator.getAndIncrement(), + nsiTypeId = NSI_TYPE.nsiTypeId, + eventId = EVENT.id, + referralDate = LocalDate.of(2000, 1, 4) + ) +} diff --git a/projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt b/projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt new file mode 100644 index 0000000000..4594faadec --- /dev/null +++ b/projects/dps-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt @@ -0,0 +1,17 @@ +package uk.gov.justice.digital.hmpps.data.generator + +import uk.gov.justice.digital.hmpps.entity.Person + +object PersonGenerator { + val DEFAULT = Person( + id = IdGenerator.getAndIncrement(), + forename = "First", + secondName = "Middle", + thirdName = null, + surname = "Last", + crn = "A000001", + nomisId = "A0001AA", + events = listOf(), + softDeleted = false + ) +} diff --git a/projects/dps-and-delius/src/dev/resources/local-public-key.pub b/projects/dps-and-delius/src/dev/resources/local-public-key.pub index c0b70f3172..c4aad98fb1 100644 --- a/projects/dps-and-delius/src/dev/resources/local-public-key.pub +++ b/projects/dps-and-delius/src/dev/resources/local-public-key.pub @@ -1,6 +1,6 @@ -----BEGIN PUBLIC KEY----- -MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo3hw1/oChbttEOxEH4NUDrH+Y -n2x0DavAmDjMbhcSiQ6+/t8Nz/N03BauWzFOGBtftnQrHfnF+O7RAKj8zMjcbIq4 -QrYeXEpnaFCGEwTtOBpxvSEWPrLEpr1gCarBQZDp67ag+SYqrDgkn2Vme/dMvMUQ -xUO3DT6jg9921J6TlwIDAQAB +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+DhFN5a7rx/KAKw9zX4dlONZY +YGrWPqjRaRlH91aLo/btXVWeXxwBiaAu1RSctoixnW0S5Wm0mpCieRjmSQH/COgC +OAsouUsuBFUy8ohKiTpQaaOPNA8KDNMNhKQ0KT7KPPCGCB1BNvVgtZInwhyVKzTL +UfvD+V/JnSwldl9uBQIDAQAB -----END PUBLIC KEY----- \ No newline at end of file diff --git a/projects/dps-and-delius/src/dev/resources/simulations/__files/document.pdf b/projects/dps-and-delius/src/dev/resources/simulations/__files/document.pdf new file mode 100644 index 0000000000000000000000000000000000000000..88d1cf8ed6a74729eab881e81a111dfcd465d394 GIT binary patch literal 14812 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%0D%VVe`V>X8`Cy`e32;=#izF4?np z4jvvy>8T%W=3JU)!c@C$e~8&%f%(`gmLz)+Zx3i#=wPhAQZL%(>-yOe8MnWjZ7JH-tzYr z_IIUTEh8ezO1;q0ny)lvo>n#z?-=ar9>kkS>@(Y_k-WSK(a^jN7Zg=d_}Jl`Da!tI zCE`U;tL8#!^LxR}D6qVjDJq3_U7xu;$hr6mM8Fa3atChJjsz-K}?BqyETF+`#gF-NoUbCK-=RhDI4EH?a zn-|MvUq9qHFk1Jk8KfvOxo@k#;Bk_oBhVH)E~6db)}*dU&--}Ej;XjUtjd7ZN?Sz*DVK2Y(wZ;31#tieaZJcOdcvj+lOAaGvc$|qIhQf7Kf-i3Kax~Ly zG@6l0jj9xb=ur#()k?(|+Js>O2(Bi+Ex;e*r^Hb6aeWg}~DP0AUymi>vw0 z#t(rBouc+^-G1VeWKJ<{{Gx&Y{!@@67>Wh;hzbdre?uW*F*qnFmWJ(3=Nmx-?uhzd zU{T6gjGa9~+R4`86!oTtf}dif01yxei<9#DJ~S{4Bm&e$qZ|!>;^I!>PQO7?K%*a{ zh6#dxqvcLvPCw==>+F6CxC5%%Bi&3-L3}#qzwvc$zeP^5Nk2q>z}lctLD64$D!*entN?qY1gjxT9i;9ei?Xv<@^MA!`e^7O zd~P5_k*rdZ#Nu9JUXCt~SQsL}%hAEfP0UMz6^=!>qQtPzr(_^2;2R72h6JnhX#>DW zT?-)V?1}=w1cdkzAS^sfL{I<>6BPl2_^?*P!azZyKro0O1Q8Pii$Os@A(^a_r|3*^ zSEP-Yj-0}e?yzqXtafO$ix?2->FFup2^Da5wFQDjMMZ%i2oM6{$8zwyc{`!uUi?mO zY~Pjq)brB;_Rda#Q@wC&XLqy&E9+lipg-p2=<=&0CpUp_a}+>0I|9AnE5S8 zE`N;fFGAP`tPu2Hg^-9}9l5x>I((ZC5&=Xxpd3%(npipTzsh0#psxN)^e^509HXNn z@RtEjZGaUKlf^<+v1nH8k0jRkBK%+oKNzY978Mf|76Xf#hy(w|^xHPS#hqRCoShvc z4V_()fAIgH{!Ks~3n8@e{zrc>F^HfTM3f%{69a+1EBS--?>c_KUjHL4fAapJ2kQbc zRg||UcFb0rkgVeg-NQD)?9G z&jttk(}f`B0C%#LVD;ihqHN&q4ro?Mb$f)XvzxOG8gS|q06##^8R32!KVWg-X{0;Y z|5e|2>R-G0Hq5_|=+x=z>S90DDa%Q^yW1nhkk%+c8>A?bUkHRIGZFg@qAtI6q9tMwnkvM8ulk8i}yx z7l8^2!VpN1kTCLlv|#oAA^St`_gVe1%D+weI~BW!V7D*eul?(<_V^wP|M$OtPx${{ z_b-kA?c|p!{*#CQn(IHY(l4I>n(LP-{*#CQn(IHY(l4I>n(LP-{*#CQn(IHY(l4I> z&0NI4Pe9m0D+yLl?D^(jPN6^1a;Cq^i~c;8N^7b8$|?chPmtddY;5Ltnyg-t6FW_^ z!4NPMEGj6B&9AwE-`bsBubd`o+`ykH=@o7uHvj#*3=AUpEl1`8fw;J_^=#j>)GPo0 zER`FmcbZyW`JeMb?%#*VA4PEh*sp5C)7m~EY#j^?fUQmZR>JqK$nkeI-w&E7_PF+| w!tXmx5DLQ +} diff --git a/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/config/FeignConfig.kt b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/config/FeignConfig.kt new file mode 100644 index 0000000000..69bd29c2c2 --- /dev/null +++ b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/config/FeignConfig.kt @@ -0,0 +1,23 @@ +package uk.gov.justice.digital.hmpps.config + +import feign.RequestInterceptor +import feign.Retryer +import org.springframework.cloud.openfeign.EnableFeignClients +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import uk.gov.justice.digital.hmpps.client.AlfrescoClient +import uk.gov.justice.digital.hmpps.security.ServiceContext + +@Configuration +@EnableFeignClients(clients = [AlfrescoClient::class]) +class FeignConfig { + @Bean + fun retryer() = Retryer.Default() + + @Bean + fun requestInterceptor() = RequestInterceptor { template -> + template.header("X-DocRepository-Remote-User", "N00") + template.header("X-DocRepository-Real-Remote-User", ServiceContext.servicePrincipal()!!.username) + template.header("Content-Type: multipart/form-data") + } +} diff --git a/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/controller/ApiController.kt b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/controller/ApiController.kt deleted file mode 100644 index e5f139965c..0000000000 --- a/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/controller/ApiController.kt +++ /dev/null @@ -1,17 +0,0 @@ -package uk.gov.justice.digital.hmpps.controller - -import org.springframework.security.access.prepost.PreAuthorize -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.PathVariable -import org.springframework.web.bind.annotation.RestController - -@RestController -class ApiController { - @PreAuthorize("hasRole('ROLE_EXAMPLE')") - @GetMapping(value = ["/example/{inputId}"]) - fun handle( - @PathVariable("inputId") inputId: String - ) { - // TODO Not yet implemented - } -} diff --git a/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/controller/DocumentController.kt b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/controller/DocumentController.kt new file mode 100644 index 0000000000..6ac37b5994 --- /dev/null +++ b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/controller/DocumentController.kt @@ -0,0 +1,33 @@ +package uk.gov.justice.digital.hmpps.controller + +import io.swagger.v3.oas.annotations.Operation +import org.springframework.http.ResponseEntity +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RestController +import uk.gov.justice.digital.hmpps.exception.NotFoundException +import uk.gov.justice.digital.hmpps.service.DocumentService + +@RestController +@PreAuthorize("hasRole('ROLE_PROBATION_API__DPS__DOCUMENTS')") +class DocumentController(private val documentService: DocumentService) { + + @GetMapping(value = ["/case/{nomisId}/documents"]) + @Operation( + summary = "List documents for a case", + description = """Returns basic personal information for the case, along with a list of person-level documents + and event-level documents. Documents are annotated with the type and description, based on what they relate + to in the probation case (e.g. a court appearance, an event, etc). + """ + ) + fun getDocuments(@PathVariable nomisId: String) = documentService.getDocumentsForCase(nomisId) + + @GetMapping(value = ["/document/{id}"]) + @Operation(summary = "Download document content") + fun downloadDocument(@PathVariable id: String) = try { + documentService.downloadDocument(id) + } catch (e: NotFoundException) { + ResponseEntity.notFound() + } +} diff --git a/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/CourtAppearance.kt b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/CourtAppearance.kt new file mode 100644 index 0000000000..32f666140f --- /dev/null +++ b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/CourtAppearance.kt @@ -0,0 +1,36 @@ +package uk.gov.justice.digital.hmpps.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import org.hibernate.annotations.Immutable +import org.hibernate.annotations.Where +import java.time.ZonedDateTime + +@Entity +@Immutable +@Where(clause = "soft_deleted = 0") +data class CourtAppearance( + @Id + @Column(name = "court_appearance_id") + val id: Long, + + @Column(name = "appearance_date") + val date: ZonedDateTime, + + @Column + val courtId: Long, + + @ManyToOne + @JoinColumn(name = "event_id") + val event: Event? = null, + + @ManyToOne + @JoinColumn(name = "outcome_id") + val outcome: ReferenceData?, + + @Column(name = "soft_deleted", columnDefinition = "number") + val softDeleted: Boolean = false +) diff --git a/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/Document.kt b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/Document.kt new file mode 100644 index 0000000000..e4fbb5ced7 --- /dev/null +++ b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/Document.kt @@ -0,0 +1,175 @@ +package uk.gov.justice.digital.hmpps.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.Id +import org.hibernate.annotations.Immutable +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query +import java.time.ZonedDateTime + +@Entity +@Immutable +data class Document( + @Id + @Column(name = "document_id") + val id: Long, + + @Column(name = "offender_id") + val personId: Long, + + @Column(name = "alfresco_document_id") + val alfrescoId: String, + + @Column + val primaryKeyId: Long, + + @Column(name = "document_name") + val name: String, + + @Column(name = "document_type") + val type: String, + + @Column + val tableName: String, + + @Column(name = "created_datetime") + val createdAt: ZonedDateTime, + + @Column + val createdByUserId: Long = 0, + + @Column + val lastUpdatedUserId: Long = 0, + + @Column(columnDefinition = "number") + val softDeleted: Boolean = false, + + // Derived fields: + val author: String? = null, + val description: String? = null, + val eventId: Long? = null +) { + fun isEventRelated() = eventId != null + fun typeDescription() = when (tableName) { + "OFFENDER" -> if (type == "PREVIOUS_CONVICTION") "PNC previous convictions" else "Offender related" + "EVENT" -> if (type == "CPS_PACK") "Crown Prosecution Service case pack" else "Sentence related" + "COURT_REPORT" -> "Court Report" + "INSTITUTIONAL_REPORT" -> "Institutional Report" + "ADDRESSASSESSMENT" -> "Address assessment related document" + "APPROVED_PREMISES_REFERRAL" -> "Approved premises referral related document" + "ASSESSMENT" -> "Assessment document" + "CASE_ALLOCATION" -> "Case allocation document" + "PERSONALCONTACT" -> "Personal contact related document" + "REFERRAL" -> "Referral related document" + "NSI" -> "Non Statutory Intervention related document" + "PERSONAL_CIRCUMSTANCE" -> "Personal circumstance related document" + "UPW_APPOINTMENT" -> "Unpaid work appointment document" + "CONTACT" -> "Contact related document" + else -> error("Un-mapped document type ($tableName/$type)") + } +} + +interface DocumentRepository : JpaRepository { + @Query( + """ + select document.document_id, + document.alfresco_document_id, + document.primary_key_id, + document.offender_id, + document.document_name, + document.document_type, + document.table_name, + document.created_datetime, + document.created_by_user_id, + document.last_updated_user_id, + document.soft_deleted, + case + when created_by.user_id is not null then created_by.forename || ' ' || created_by.surname + when updated_by.user_id is not null then updated_by.forename || ' ' || updated_by.surname + end as author, + case + when address_assessment.address_assessment_id is not null + then 'Address assessment on ' || to_char(address_assessment.assessment_date, 'dd/MM/yyyy') + when approved_premises_referral.approved_premises_referral_id is not null + then 'Approved premises referral on ' || to_char(approved_premises_referral.referral_date, 'dd/MM/yyyy') + when assessment.assessment_id is not null + then 'Assessment for ' || r_assessment_type.description || ' on ' || to_char(assessment.assessment_date, 'dd/MM/yyyy') + when contact.contact_id is not null + then 'Contact on ' || to_char(contact.contact_date, 'dd/MM/yyyy') || ' for ' || r_contact_type.description + when court_report.court_report_id is not null + then r_court_report_type.description || ' requested by ' || court.court_name || ' on ' || to_char(court_report.date_requested, 'dd/MM/yyyy') + when institutional_report.institutional_report_id is not null + then institutional_report_type.code_description || ' at ' || r_institution.institution_name || ' requested on ' || to_char(institutional_report.date_requested, 'dd/MM/yyyy') + when nsi.nsi_id is not null + then 'Non Statutory Intervention for ' || r_nsi_type.description || ' on ' || to_char(nsi.referral_date, 'dd/MM/yyyy') + when personal_circumstance.personal_circumstance_id is not null + then 'Personal circumstance of ' || r_circumstance_type.code_description || ' started on ' || to_char(personal_circumstance.start_date, 'dd/MM/yyyy') + when personal_contact.personal_contact_id is not null + then 'Personal contact of type ' || personal_contact_relationship_type.code_description || ' with ' || personal_contact.relationship + when referral.referral_id is not null + then 'Referral for ' || r_referral_type.description || ' on ' || to_char(referral.referral_date, 'dd/MM/yyyy') + when upw_appointment.upw_appointment_id is not null + then 'Unpaid work appointment on ' || to_char(upw_appointment.appointment_date, 'dd/MM/yyyy') || ' for ' || upw_project.name + end as description, + coalesce( + event.event_id, + court_appearance.event_id, + institutional_report_disposal.event_id, + approved_premises_referral.event_id, + assessment_referral.event_id, + case_allocation.event_id, + referral.event_id, + upw_appointment_disposal.event_id, + contact.event_id, + nsi.event_id + ) as event_id + from document + -- the following joins are to get the event_id from the related entities, for event-level documents + left join event on document.table_name = 'EVENT' and document.primary_key_id = event.event_id + left join court_report on document.table_name = 'COURT_REPORT' and document.primary_key_id = court_report.court_report_id + left join court_appearance on court_report.court_appearance_id = court_appearance.court_appearance_id + left join court on court.court_id = court_appearance.court_id + left join r_court_report_type on r_court_report_type.court_report_type_id = court_report.court_report_type_id + left join institutional_report on document.table_name = 'INSTITUTIONAL_REPORT' and document.primary_key_id = institutional_report.institutional_report_id + left join custody on institutional_report.custody_id = custody.custody_id + left join disposal institutional_report_disposal on institutional_report_disposal.disposal_id = custody.disposal_id + left join r_institution on r_institution.institution_id = institutional_report.institution_id and r_institution.establishment = institutional_report.establishment + left join r_standard_reference_list institutional_report_type on institutional_report_type.standard_reference_list_id = institutional_report.institution_report_type_id + left join approved_premises_referral on document.table_name = 'APPROVED_PREMISES_REFERRAL' and document.primary_key_id = approved_premises_referral.approved_premises_referral_id + left join assessment on document.table_name = 'ASSESSMENT' and document.primary_key_id = assessment.assessment_id and assessment.referral_id is not null + left join r_assessment_type on assessment.assessment_type_id = r_assessment_type.assessment_type_id + left join referral assessment_referral on assessment.referral_id = assessment_referral.referral_id + left join case_allocation on document.table_name = 'CASE_ALLOCATION' and document.primary_key_id = case_allocation.case_allocation_id + left join referral on document.table_name = 'REFERRAL' and document.primary_key_id = referral.referral_id + left join r_referral_type on r_referral_type.referral_type_id = referral.referral_type_id + left join upw_appointment on document.table_name = 'UPW_APPOINTMENT' and document.primary_key_id = upw_appointment.upw_appointment_id + left join upw_details on upw_details.upw_details_id = upw_appointment.upw_details_id + left join upw_project on upw_project.upw_project_id = upw_appointment.upw_project_id + left join disposal upw_appointment_disposal on upw_appointment_disposal.disposal_id = upw_details.disposal_id + left join contact on document.table_name = 'CONTACT' and document.primary_key_id = contact.contact_id + left join r_contact_type on r_contact_type.contact_type_id = contact.contact_type_id + left join nsi on document.table_name = 'NSI' and document.primary_key_id = nsi.nsi_id + left join r_nsi_type on r_nsi_type.nsi_type_id = nsi.nsi_type_id + -- the following joins are to get extra info for the description of offender-level docs + left join address_assessment on document.table_name = 'ADDRESSASSESSMENT' and document.primary_key_id = address_assessment.address_assessment_id + left join personal_circumstance on document.table_name = 'PERSONAL_CIRCUMSTANCE' and document.primary_key_id = personal_circumstance.personal_circumstance_id + left join r_circumstance_type on r_circumstance_type.circumstance_type_id = personal_circumstance.circumstance_type_id + left join personal_contact on document.table_name = 'PERSONALCONTACT' and document.primary_key_id = personal_contact.personal_contact_id + left join r_standard_reference_list personal_contact_relationship_type on personal_contact_relationship_type.standard_reference_list_id = personal_contact.relationship_type_id + -- the following joins are to populate the author field + left join user_ created_by on created_by.user_id = document.created_by_user_id + left join user_ updated_by on updated_by.user_id = document.last_updated_user_id + where document.offender_id = :personId + and document.soft_deleted = 0 + and document.table_name in ('OFFENDER', 'ADDRESSASSESSMENT', 'PERSONALCONTACT', 'PERSONAL_CIRCUMSTANCE', + 'EVENT', 'COURT_REPORT', 'INSTITUTIONAL_REPORT', 'APPROVED_PREMISES_REFERRAL', 'ASSESSMENT', 'CASE_ALLOCATION', 'REFERRAL', 'UPW_APPOINTMENT', + 'CONTACT', 'NSI') + """, + nativeQuery = true + ) + fun getPersonAndEventDocuments(personId: Long): List + + @Query("select d.name from Document d where d.alfrescoId = :alfrescoId") + fun findNameByAlfrescoId(alfrescoId: String): String? +} diff --git a/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/Event.kt b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/Event.kt new file mode 100644 index 0000000000..54ab8d50b1 --- /dev/null +++ b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/Event.kt @@ -0,0 +1,122 @@ +package uk.gov.justice.digital.hmpps.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.JoinColumns +import jakarta.persistence.ManyToOne +import jakarta.persistence.OneToMany +import jakarta.persistence.OneToOne +import jakarta.persistence.Table +import org.hibernate.annotations.Immutable +import org.hibernate.annotations.Where +import java.time.LocalDate + +@Entity +@Immutable +@Where(clause = "soft_deleted = 0") +data class Event( + @Id + @Column(name = "event_id") + val id: Long, + + @ManyToOne + @JoinColumn(name = "offender_id") + val person: Person, + + @Column + val referralDate: LocalDate, + + @OneToOne(mappedBy = "event") + val mainOffence: MainOffence, + + @OneToMany(mappedBy = "event") + val courtAppearances: List, + + @OneToOne(mappedBy = "event") + val disposal: Disposal? = null, + + @Column(name = "active_flag", columnDefinition = "number") + val active: Boolean = true, + + @Column(columnDefinition = "number") + val softDeleted: Boolean = false +) + +@Entity +@Immutable +@Where(clause = "soft_deleted = 0") +data class Disposal( + @Id + @Column(name = "disposal_id") + val id: Long, + + @OneToOne + @JoinColumn(name = "event_id") + val event: Event, + + @ManyToOne + @JoinColumn(name = "disposal_type_id") + val type: DisposalType, + + @Column(name = "entry_length") + val length: Long?, + + @ManyToOne + @JoinColumn(name = "entry_length_units_id") + val lengthUnits: ReferenceData?, + + @OneToOne(mappedBy = "disposal") + val custody: Custody? = null, + + @Column(name = "active_flag", columnDefinition = "number") + val active: Boolean = true, + + @Column(columnDefinition = "number") + val softDeleted: Boolean = false +) + +@Entity +@Immutable +@Table(name = "r_disposal_type") +data class DisposalType( + @Id + @Column(name = "disposal_type_id") + val id: Long, + + @Column + val description: String +) + +@Entity +data class Custody( + @Id + @Column(name = "custody_id") + val id: Long, + + @OneToOne + @JoinColumn(name = "disposal_id") + val disposal: Disposal, + + @ManyToOne + @JoinColumns( + JoinColumn(name = "institution_id", referencedColumnName = "institution_id"), + JoinColumn(name = "establishment", referencedColumnName = "establishment") + ) + val institution: Institution +) + +@Entity +@Table(name = "r_institution") +data class Institution( + @Id + @Column(name = "institution_id") + val id: Long, + + @Column(name = "institution_name") + val name: String, + + @Column + val establishment: String +) diff --git a/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/Offence.kt b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/Offence.kt new file mode 100644 index 0000000000..030c337d5d --- /dev/null +++ b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/Offence.kt @@ -0,0 +1,43 @@ +package uk.gov.justice.digital.hmpps.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.OneToOne +import jakarta.persistence.Table +import org.hibernate.annotations.Immutable +import org.hibernate.annotations.Where + +@Entity +@Immutable +@Where(clause = "soft_deleted = 0") +data class MainOffence( + @Id + @Column(name = "main_offence_id") + val id: Long, + + @OneToOne + @JoinColumn(name = "event_id") + val event: Event? = null, + + @ManyToOne + @JoinColumn(name = "offence_id") + val offence: Offence, + + @Column(columnDefinition = "number") + val softDeleted: Boolean = false +) + +@Immutable +@Entity +@Table(name = "r_offence") +data class Offence( + @Id + @Column(name = "offence_id") + val id: Long, + + @Column + val subCategoryDescription: String +) diff --git a/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/Person.kt b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/Person.kt new file mode 100644 index 0000000000..fdd290ef7a --- /dev/null +++ b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/Person.kt @@ -0,0 +1,49 @@ +package uk.gov.justice.digital.hmpps.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.Id +import jakarta.persistence.OneToMany +import jakarta.persistence.Table +import org.hibernate.annotations.Immutable +import org.hibernate.annotations.Where +import org.springframework.data.jpa.repository.JpaRepository + +@Entity +@Immutable +@Table(name = "offender") +@Where(clause = "soft_deleted = 0") +data class Person( + @Id + @Column(name = "offender_id") + val id: Long, + + @Column(name = "first_name") + val forename: String, + + @Column(name = "second_name") + val secondName: String?, + + @Column(name = "third_name") + val thirdName: String?, + + @Column(name = "surname") + val surname: String, + + @Column(columnDefinition = "char(7)") + val crn: String, + + @Column(name = "noms_number", columnDefinition = "char(7)") + val nomisId: String?, + + @OneToMany(mappedBy = "person", fetch = FetchType.EAGER) + val events: List, + + @Column(columnDefinition = "number") + val softDeleted: Boolean = false +) + +interface PersonRepository : JpaRepository { + fun findByNomisId(nomisId: String): Person? +} diff --git a/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/ReferenceData.kt b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/ReferenceData.kt new file mode 100644 index 0000000000..8a9ec1593a --- /dev/null +++ b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/ReferenceData.kt @@ -0,0 +1,22 @@ +package uk.gov.justice.digital.hmpps.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.Id +import jakarta.persistence.Table +import org.hibernate.annotations.Immutable + +@Entity +@Immutable +@Table(name = "r_standard_reference_list") +class ReferenceData( + @Id + @Column(name = "standard_reference_list_id", nullable = false) + val id: Long, + + @Column(name = "code_value", length = 100, nullable = false) + val code: String, + + @Column(name = "code_description", length = 500, nullable = false) + val description: String +) diff --git a/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/model/Conviction.kt b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/model/Conviction.kt new file mode 100644 index 0000000000..af0d8d9d49 --- /dev/null +++ b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/model/Conviction.kt @@ -0,0 +1,12 @@ +package uk.gov.justice.digital.hmpps.model + +import java.time.LocalDate + +data class Conviction( + val title: String?, + val offence: String, + val date: LocalDate, + val active: Boolean, + val documents: List, + val institutionName: String? +) diff --git a/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/model/Document.kt b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/model/Document.kt new file mode 100644 index 0000000000..d0cc3ac3a8 --- /dev/null +++ b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/model/Document.kt @@ -0,0 +1,12 @@ +package uk.gov.justice.digital.hmpps.model + +import java.time.ZonedDateTime + +data class Document( + val id: String, + val name: String, + val description: String?, + val type: String, + val author: String?, + val createdAt: ZonedDateTime +) diff --git a/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/model/Name.kt b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/model/Name.kt new file mode 100644 index 0000000000..1c3ec9195e --- /dev/null +++ b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/model/Name.kt @@ -0,0 +1,7 @@ +package uk.gov.justice.digital.hmpps.model + +data class Name( + val forename: String, + val middleName: String?, + val surname: String +) diff --git a/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/model/ProbationDocumentsResponse.kt b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/model/ProbationDocumentsResponse.kt new file mode 100644 index 0000000000..0597980aea --- /dev/null +++ b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/model/ProbationDocumentsResponse.kt @@ -0,0 +1,8 @@ +package uk.gov.justice.digital.hmpps.model + +data class ProbationDocumentsResponse( + val crn: String, + val name: Name, + val documents: List, + val convictions: List +) diff --git a/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/DocumentService.kt b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/DocumentService.kt new file mode 100644 index 0000000000..4b1e315a9a --- /dev/null +++ b/projects/dps-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/DocumentService.kt @@ -0,0 +1,95 @@ +package uk.gov.justice.digital.hmpps.service + +import org.springframework.core.io.Resource +import org.springframework.http.ContentDisposition +import org.springframework.http.HttpHeaders +import org.springframework.http.HttpHeaders.CONTENT_DISPOSITION +import org.springframework.http.ResponseEntity +import org.springframework.stereotype.Service +import uk.gov.justice.digital.hmpps.client.AlfrescoClient +import uk.gov.justice.digital.hmpps.entity.CourtAppearance +import uk.gov.justice.digital.hmpps.entity.Disposal +import uk.gov.justice.digital.hmpps.entity.DocumentRepository +import uk.gov.justice.digital.hmpps.entity.PersonRepository +import uk.gov.justice.digital.hmpps.exception.NotFoundException +import uk.gov.justice.digital.hmpps.model.Conviction +import uk.gov.justice.digital.hmpps.model.Document +import uk.gov.justice.digital.hmpps.model.Name +import uk.gov.justice.digital.hmpps.model.ProbationDocumentsResponse +import kotlin.text.Charsets.UTF_8 + +@Service +class DocumentService( + private val personRepository: PersonRepository, + private val documentRepository: DocumentRepository, + private val alfrescoClient: AlfrescoClient +) { + fun downloadDocument(id: String): ResponseEntity { + val filename = documentRepository.findNameByAlfrescoId(id) ?: throw NotFoundException("Document", "alfrescoId", id) + val response = alfrescoClient.getDocument(id) + return when { + response.statusCode.is2xxSuccessful -> ResponseEntity.ok() + .headers { it.putAll(response.sanitisedHeaders()) } + .header(CONTENT_DISPOSITION, ContentDisposition.attachment().filename(filename, UTF_8).build().toString()) + .body(response.body) + + response.statusCode.is4xxClientError -> throw NotFoundException("Document content", "alfrescoId", id) + + else -> throw RuntimeException("Failed to download document. Alfresco responded with ${response.statusCode}.") + } + } + + fun getDocumentsForCase(nomisId: String) = personRepository.findByNomisId(nomisId)?.let { person -> + val documents = documentRepository.getPersonAndEventDocuments(person.id) + val eventDocuments = documents.filter { it.isEventRelated() }.groupBy { it.eventId } + ProbationDocumentsResponse( + crn = person.crn, + name = Name( + person.forename, + listOfNotNull(person.secondName, person.thirdName).joinToString(" "), + person.surname + ), + documents = documents.filter { !it.isEventRelated() }.map { document -> + Document( + id = document.alfrescoId, + name = document.name, + description = document.description, + type = document.typeDescription(), + author = document.author, + createdAt = document.createdAt + ) + }.sortedBy { it.createdAt }, + convictions = person.events.map { event -> + Conviction( + title = event.disposal?.description ?: event.courtAppearances.latestOutcome()?.description, + offence = event.mainOffence.offence.subCategoryDescription, + date = event.referralDate, + active = event.active, + institutionName = event.disposal?.custody?.institution?.name, + documents = eventDocuments[event.id]?.map { document -> + Document( + id = document.alfrescoId, + name = document.name, + description = document.description, + type = document.typeDescription(), + author = document.author, + createdAt = document.createdAt + ) + }?.sortedBy { it.createdAt } ?: emptyList() + ) + }.sortedBy { it.date } + ) + } ?: throw NotFoundException("Person", "nomisId", nomisId) + + private val Disposal.description get() = "${type.description}${lengthString?.let { " ($it)" } ?: ""}" + private val Disposal.lengthString get() = length?.let { "$length ${lengthUnits!!.description}" } + private fun List.latestOutcome() = filter { it.outcome != null }.maxByOrNull { it.date }?.outcome + private fun ResponseEntity.sanitisedHeaders() = headers.filterKeys { + it in listOf( + HttpHeaders.CONTENT_LENGTH, + HttpHeaders.CONTENT_TYPE, + HttpHeaders.ETAG, + HttpHeaders.LAST_MODIFIED + ) + } +} diff --git a/projects/dps-and-delius/src/main/resources/application.yml b/projects/dps-and-delius/src/main/resources/application.yml index 6b8ae0e469..4e3c0a0a30 100644 --- a/projects/dps-and-delius/src/main/resources/application.yml +++ b/projects/dps-and-delius/src/main/resources/application.yml @@ -26,10 +26,20 @@ spring: provider: hmpps-auth: token-uri: http://localhost:${wiremock.port}/auth/oauth/token + cloud.openfeign.client.config: + default: + logger-level: full + connect-timeout: 5000 + read-timeout: 5000 + default-request-headers: + Accept: application/json + Content-Type: application/json springdoc.default-produces-media-type: application/json delius.db.username: DpsAndDelius # Should match value in [deploy/database/access.yml]. +integrations.alfresco.url: http://localhost:${wiremock.port}/alfresco + management.endpoints.web: base-path: / exposure.include: [ "health", "info" ]