diff --git a/.github/actions/render-project-template/action.yml b/.github/actions/render-project-template/action.yml index 7f6679513b..bb6864bed0 100644 --- a/.github/actions/render-project-template/action.yml +++ b/.github/actions/render-project-template/action.yml @@ -31,7 +31,6 @@ runs: sed -i '/add new projects here/i \ - '"'"'["${{ inputs.project_name }}"]'"'"'' .github/workflows/access.yml sed -i '/add new projects here/i \ - '"'"'["${{ inputs.project_name }}"]'"'"'' .github/workflows/deploy.yml sed -i '/add new projects here/i \ - '"'"'["${{ inputs.project_name }}"]'"'"'' .github/workflows/docs.yml - sed -i '/add new projects here/i \ - ${{ inputs.project_name }}' .github/workflows/suppress-trivy.yml sed -i '/add new projects here/i \ - ${{ inputs.project_name }}' .github/workflows/build.yml sed -i '/add new projects here/i \* [${{ steps.project_name.outputs.title_case }}](https://ministryofjustice.github.io/hmpps-probation-integration-services/tech-docs/projects/${{ inputs.project_name }})' doc/tech-docs/source/services.html.md.erb sed 's/$SERVICE_NAME/${{ inputs.project_name }}/g' templates/runConfiguration.xml > '.idea/runConfigurations/${{ steps.project_name.outputs.underscore }}.xml' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 4e63839e9a..55ba29fc46 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -175,7 +175,7 @@ jobs: - build-projects steps: - uses: actions/checkout@v4 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v3 with: path: artifacts diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 3a31f9d580..b2a6c81b6f 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -26,7 +26,7 @@ jobs: java-version: '21' distribution: 'temurin' - name: Setup GPG commit verification - uses: crazy-max/ghaction-import-gpg@82a020f1f7f605c65dd2449b392a52c3fcfef7ef # v6.0.0 + uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0 with: gpg_private_key: ${{ secrets.BOT_GPG_PRIVATE_KEY }} passphrase: ${{ secrets.BOT_GPG_PASSPHRASE }} diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index b2301c5663..0648e02c05 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -37,7 +37,7 @@ jobs: image-ref: 'ghcr.io/ministryofjustice/hmpps-probation-integration-services/${{ matrix.project }}:latest' ignore-unfixed: true severity: 'CRITICAL,HIGH' - exit-code: '1' + exit-code: '0' format: 'sarif' output: 'trivy-results.sarif' trivyignores: '.trivyignore,projects/${{ matrix.project }}/.trivyignore' @@ -50,7 +50,6 @@ jobs: sarif_file: 'trivy-results.sarif' - name: Get Trivy results - if: always() uses: aquasecurity/trivy-action@91713af97dc80187565512baba96e4364e983601 # v0.16.0 with: image-ref: 'ghcr.io/ministryofjustice/hmpps-probation-integration-services/${{ matrix.project }}:latest' @@ -60,44 +59,77 @@ jobs: output: 'results.json' trivyignores: '.trivyignore,projects/${{ matrix.project }}/.trivyignore' - - name: Output Trivy results - if: always() - run: cat results.json + - name: Output results + id: results + run: echo "vulnerabilities=$(jq -c '.Results[].Vulnerabilities | select(. != null) | flatten' results.json)" | tee -a "$GITHUB_OUTPUT" + env: + GITHUB_TOKEN: ${{ github.token }} + + - name: Merge outputs + uses: cloudposse/github-action-matrix-outputs-write@928e2a2d3d6ae4eb94010827489805c17c81181f # v0.4.2 + with: + matrix-step-name: trivy + matrix-key: ${{ matrix.project }} + outputs: | + vulnerabilities: ${{ steps.results.outputs.vulnerabilities }} + + trivy-merge: + runs-on: ubuntu-latest + needs: + - trivy-scan + steps: + - uses: actions/checkout@v4 + + - uses: cloudposse/github-action-matrix-outputs-read@ea1c28d66c34b8400391ed74d510f66abc392d5e # v0.1.1 + id: trivy + with: + matrix-step-name: trivy - name: Create GitHub issues - if: failure() run: | - jq -c '.Results[].Vulnerabilities | select(. != null) | flatten | .[]' results.json | while read -r vulnerability; do - id=$(echo "$vulnerability" | jq -r '.VulnerabilityID') - if [[ $(gh issue list --state open --label dependencies --label security --search "$id (${{ matrix.project }})" | wc -l) -gt 0 ]]; then - echo "Issue '$id (${{ matrix.project }})' already exists" + echo "$result" | jq -c '[.vulnerabilities | to_entries[] | .key as $project | .value // empty | map(. + {Projects: [$project]})] + | flatten + | group_by(.VulnerabilityID) + | map(reduce .[] as $vuln (.[0] + {Locations:[]}; .Projects += $vuln.Projects | .Locations += [$vuln.PkgName + ":" + $vuln.InstalledVersion + " (" + $vuln.PkgPath + ")"])) + | map_values({Title: .VulnerabilityID, Body: ("### " + .Title + "\n" + .PrimaryURL + "\n>" + .Description + "\n#### Projects:\n* " + (.Projects | sort | unique | join("\n* ")) + "\n#### Locations:\n* `" + (.Locations | sort | unique | join("`\n* `")) + "`\n#### References:\n* " + (.References | sort | unique | join("\n* ")))}) + | .[]' \ + | while read -r vulnerability; do + title=$(echo "$vulnerability" | jq -r '.Title') + body=$(echo "$vulnerability" | jq -r '.Body') + existing=$(gh issue list --state open --label dependencies --label security --search "$title" --json 'number' --jq '.[].number' | head -n1) + if [ -n "$existing" ]; then + echo "Issue '$title' already exists, updating body..." + gh issue edit "$existing" --body "$body" else - gh issue create \ - --title "$id (${{ matrix.project }})" \ - --body "$(echo "$vulnerability" | jq -r '.Title + "\n* Project: ${{ matrix.project }}\n* Package: `" + .PkgName + ":" + .InstalledVersion + "`\n* Location: `" + .PkgPath + "`\n\n>" + .Description + "\n\n" + .PrimaryURL + "\n\nIf the vulnerability does not impact the `${{ matrix.project }}` project, you can suppress this alert by adding a comment starting with `Suppress`. For example, \"Suppressed because we do not process any untrusted XML content\"."')" \ - --label 'dependencies,security' + gh issue create --title "$title" --body "$body" --label 'dependencies,security' fi done env: GITHUB_TOKEN: ${{ github.token }} + result: ${{ steps.trivy.outputs.result }} - - name: Check & Close GH Issue - if: always() + - name: Close GitHub issues run: | - openissues="$(gh issue list --state open --label dependencies --label security --search '(${{ matrix.project }})' | awk '{print $3}')" - scanresults="$(jq -r -c '.Results[].Vulnerabilities | select(. != null) | flatten | .[].VulnerabilityID' results.json)" + openissues="$(gh issue list --state open --label dependencies --label security | awk '{print $3}')" + scanresults="$(echo "$result" | jq -r -c '.vulnerabilities | with_entries(select(.value != null)) | .[][].VulnerabilityID' | sort -u)" issuestoclose="$(comm -23 <(echo "$openissues" | sort -u) <(echo "$scanresults" | sort -u))" #print lines only present in first file echo "openissues=$openissues" echo "scanresults=$scanresults" echo "issuestoclose=$issuestoclose" for cve in $issuestoclose; do echo "$cve is already resolved, removing matching issue..." - issuenumber=$(gh issue list --state open --label dependencies --label security --search "$cve (${{ matrix.project }})" | awk '{print $1}') + issuenumber=$(gh issue list --state open --label dependencies --label security --search "$cve" | awk '{print $1}') echo "$issuenumber" | xargs -n1 gh issue close done - env: GITHUB_TOKEN: ${{ github.token }} + result: ${{ steps.trivy.outputs.result }} + + - name: Fail job if any vulnerabilities are found + if: steps.trivy.outputs.result != '{}' + run: if [ "$(echo "$result" | jq '.vulnerabilities | with_entries(select(.value != null)) | length')" != 0 ]; then exit 1; fi + env: + result: ${{ steps.trivy.outputs.result }} veracode-scan: runs-on: ubuntu-latest diff --git a/.github/workflows/suppress-trivy.yml b/.github/workflows/suppress-trivy.yml index 81648db6e8..e69de29bb2 100644 --- a/.github/workflows/suppress-trivy.yml +++ b/.github/workflows/suppress-trivy.yml @@ -1,116 +0,0 @@ -name: Suppress Trivy alert - -on: - issue_comment: - types: - - created - workflow_dispatch: - inputs: - cve_id: - description: CVE ID - type: string - required: true - project: - description: Project - type: choice - required: true - options: - - approved-premises-and-delius - - approved-premises-and-oasys - - arns-and-delius - - assessment-summary-and-delius - - cas3-and-delius - - court-case-and-delius - - create-and-vary-a-licence-and-delius - - custody-key-dates-and-delius - - domain-events-and-delius - - dps-and-delius - - effective-proposal-framework-and-delius - - external-api-and-delius - - hdc-licences-and-delius - - hmpps-auth-and-delius - - make-recall-decisions-and-delius - - manage-offences-and-delius - - manage-pom-cases-and-delius - - offender-events-and-delius - - opd-and-delius - - pathfinder-and-delius - - person-search-index-from-delius - - pre-sentence-reports-to-delius - - prison-case-notes-to-probation - - prison-custody-status-to-delius - - prison-education-and-delius - - prison-identifier-and-delius - - prisoner-profile-and-delius - - redrive-dead-letter-queues - - refer-and-monitor-and-delius - - resettlement-passport-and-delius - - risk-assessment-scores-to-delius - - sentence-plan-and-delius - - sentence-plan-and-oasys - - soc-and-delius - - tier-to-delius - - unpaid-work-and-delius - - workforce-allocations-to-delius - # ^ add new projects here - # GitHub Actions doesn't support dynamic choices, we must add each project here to enable manual deployments - # See https://github.com/community/community/discussions/11795 - reason: - description: Reason - type: string - required: true - -jobs: - build: - if: github.event_name=='workflow_dispatch' || (startsWith(github.event.comment.body, 'Suppress') && startsWith(github.event.issue.title, 'CVE-')) - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v4 - with: - ref: main - - - name: Set CVE from Issue Title automatically - if: github.event_name=='issue_comment' - run: | - echo "cve_id=$(echo "$TITLE" | sed -E 's/(.*) .*/\1/')" | tee -a "$GITHUB_ENV" # CVE-123 - echo "project=$(echo "$TITLE" | sed -E 's/.* \((.*)\)/\1/')" | tee -a "$GITHUB_ENV" # project-name - echo "reason=$COMMENT" | tee -a "$GITHUB_ENV" - env: - TITLE: ${{ github.event.issue.title }} - COMMENT: ${{ github.event.comment.body }} - REASON: ${{ inputs.reason }} - - - name: Set CVE from manual input - if: github.event_name=='workflow_dispatch' - run: | - echo "cve_id=$CVE_ID" | tee -a "$GITHUB_ENV" - echo "project=$PROJECT" | tee -a "$GITHUB_ENV" - echo "reason=$REASON" | tee -a "$GITHUB_ENV" - env: - CVE_ID: ${{ inputs.cve_id }} - PROJECT: ${{ inputs.project }} - REASON: ${{ inputs.reason }} - - - name: Update trivyignore file and create pull request - run: | - git switch -c "suppress/${CVE_ID}_${PROJECT}" - git push origin "suppress/${CVE_ID}_${PROJECT}" - git pull origin "suppress/${CVE_ID}_${PROJECT}" - git push origin "suppress/${CVE_ID}_${PROJECT}" - echo -e "\n# ${REASON} by ${ACTOR}\n${CVE_ID} exp:$(date -d '+2 weeks' '+%Y-%m-%d')" >> "projects/${PROJECT}/.trivyignore" - gh api --method PUT "/repos/ministryofjustice/hmpps-probation-integration-services/contents/projects/${PROJECT}/.trivyignore" \ - --field message="Suppress ${CVE_ID} in ${PROJECT}" \ - --field content=@<( base64 -i "projects/${PROJECT}/.trivyignore" ) \ - --field branch="suppress/${CVE_ID}_${PROJECT}" \ - --field sha="$( git rev-parse "suppress/${CVE_ID}_${PROJECT}:projects/${PROJECT}/.trivyignore" )" - gh pr create \ - --title "Suppress ${CVE_ID} in ${PROJECT}" \ - --body "Suppressed by ${ACTOR} with reason: ${REASON}" \ - --label 'dependencies,security' - env: - GITHUB_TOKEN: ${{ github.token }} - CVE_ID: ${{ env.cve_id }} - PROJECT: ${{ env.project }} - REASON: ${{ env.reason }} - ACTOR: ${{ github.actor }} diff --git a/.trivyignore b/.trivyignore index 1a01b9e86c..e69de29bb2 100644 --- a/.trivyignore +++ b/.trivyignore @@ -1,10 +0,0 @@ - -# Vulnerability in Logback, which is used by the following dependencies: -# -# * Application Insights Java agent -# Not exploitable in the context of the Application Insights Java agent. -# Reference: https://github.com/microsoft/ApplicationInsights-Java/issues/3414#issuecomment-1833988304 -# * Spring Boot -# Only exploitable if logback receiver component is deployed. This is not the case by default in Spring Boot. -# Reference: https://github.com/spring-projects/spring-boot/issues/38643#issuecomment-1838497420 -CVE-2023-6378 exp:2024-01-12 diff --git a/README.md b/README.md index cc0326dccd..b6f838408b 100644 --- a/README.md +++ b/README.md @@ -324,32 +324,6 @@ generic-service: SERVICE_URL: https://example.com ``` -### Secrets -Add secrets for each environment here: https://github.com/ministryofjustice/hmpps-probation-integration-services/settings/secrets/actions. - -The deployment job pushes GitHub secrets to the Kubernetes namespaces. Then at runtime, Kubernetes passes these secrets -as environment variables to the container. - -GitHub secret names should be uppercase and prefixed with the project name. ( -e.g. `PRISON_CASE_NOTES_TO_PROBATION_CLIENT_ID`). The project name will be used as the Kubernetes secret name, and -anything after it will be used as the key. - -For example, `PRISON_CASE_NOTES_TO_PROBATION_CLIENT_ID` will be converted into a secret -named `prison-case-notes-to-probation`, containing a key of `CLIENT_ID`. This can be referenced in the `values*.yml` -files like so: -```yaml -namespace_secrets: - prison-case-notes-to-probation: - MY_ENV_VAR: CLIENT_ID -``` - -Additionally, there are some shared values defined in a `common` secret in each namespace. This secret contains: -```yaml -APPLICATIONINSIGHTS_CONNECTION_STRING: Connection string for pushing Telemetry to Azure Application Insights. -DB_URL: URL for the Delius probation database -DB_STANDBY_URL: URL for the standby (read-only) Delius probation database -``` - ## Accessing the Delius Database To configure access to the Delius probation database, add an `access.yml` file to the project's `deploy/database` folder. diff --git a/build.gradle.kts b/build.gradle.kts index c31a13dc6b..cc9ba442f5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,11 +6,11 @@ import uk.gov.justice.digital.hmpps.plugins.ClassPathPlugin import uk.gov.justice.digital.hmpps.plugins.JibConfigPlugin plugins { - kotlin("jvm") version "1.9.21" - kotlin("plugin.spring") version "1.9.21" apply false - kotlin("plugin.jpa") version "1.9.21" apply false - kotlin("kapt") version "1.9.21" apply false - id("org.springframework.boot") version "3.2.0" apply false + kotlin("jvm") version "1.9.22" + kotlin("plugin.spring") version "1.9.22" apply false + kotlin("plugin.jpa") version "1.9.22" apply false + kotlin("kapt") version "1.9.22" apply false + id("org.springframework.boot") version "3.2.1" apply false id("io.spring.dependency-management") version "1.1.4" apply false id("com.google.cloud.tools.jib") apply false id("base") @@ -20,7 +20,7 @@ plugins { val agentDeps: Configuration by configurations.creating dependencies { - agentDeps("com.microsoft.azure:applicationinsights-agent:3.4.18") + agentDeps("com.microsoft.azure:applicationinsights-agent:3.4.19") } val copyAgentTask = project.tasks.register("copyAgent") { diff --git a/projects/assessment-summary-and-delius/src/dev/resources/simulations/__files/assessment-summary-H123456.json b/projects/assessment-summary-and-delius/src/dev/resources/simulations/__files/assessment-summary-H123456.json index 269a151800..da91707e15 100644 --- a/projects/assessment-summary-and-delius/src/dev/resources/simulations/__files/assessment-summary-H123456.json +++ b/projects/assessment-summary-and-delius/src/dev/resources/simulations/__files/assessment-summary-H123456.json @@ -27,7 +27,7 @@ "riskStaffCustody": "Low", "riskStaffCommunity": "Low", "riskKnownAdultCustody": "Low", - "riskAdultCommunity": "Medium", + "riskKnownAdultCommunity": "Medium", "riskPublicCustody": "Low", "riskPublicCommunity": "High", "riskChildrenCustody": "Low", @@ -88,34 +88,35 @@ "sentencePlan": { "objectives": [ { - "objectiveCode": "Increased knowledge of physical/ psychological/ emotional self harm linked to drug use", - "objectiveMeasure": "RII", + "objectiveCodeDesc": "Increased knowledge of physical/ psychological/ emotional self harm linked to drug use", "objectiveSequence": 1, "criminogenicNeeds": [ { - "criminogenicNeed": "IHD" + "criminogenicNeed": "IHD", + "criminogenicNeedDesc": "Risk to Public" } ], "actions": [ { - "action": "VI9", - "actionComment": null + "actionDesc": "Drug counselling", + "actionComment": "Comments about the action" } ] }, { - "objectiveCode": "Improve employment related skills", - "objectiveMeasure": "RII", + "objectiveCodeDesc": "Improve employment related skills", "objectiveSequence": 2, "criminogenicNeeds": [ { - "criminogenicNeed": "I4" + "criminogenicNeed": "I4", + "criminogenicNeedDesc": "Education Training and Employment" } ], "actions": [ { "action": "VIII1", - "actionComment": null + "actionDesc": "Basic skills", + "actionComment": "Some comment about their basic skills" } ] } diff --git a/projects/assessment-summary-and-delius/src/dev/resources/simulations/__files/assessment-summary-L123456.json b/projects/assessment-summary-and-delius/src/dev/resources/simulations/__files/assessment-summary-L123456.json index 6f9e4d7a63..02f3db3919 100644 --- a/projects/assessment-summary-and-delius/src/dev/resources/simulations/__files/assessment-summary-L123456.json +++ b/projects/assessment-summary-and-delius/src/dev/resources/simulations/__files/assessment-summary-L123456.json @@ -35,7 +35,7 @@ "riskStaffCustody": null, "riskStaffCommunity": "Low", "riskKnownAdultCustody": null, - "riskAdultCommunity": null, + "riskKnownAdultCommunity": null, "riskPublicCustody": "Low", "riskPublicCommunity": null, "riskChildrenCustody": "Low", diff --git a/projects/assessment-summary-and-delius/src/dev/resources/simulations/__files/assessment-summary-M123456.json b/projects/assessment-summary-and-delius/src/dev/resources/simulations/__files/assessment-summary-M123456.json index 111494c165..5b3846fd2e 100644 --- a/projects/assessment-summary-and-delius/src/dev/resources/simulations/__files/assessment-summary-M123456.json +++ b/projects/assessment-summary-and-delius/src/dev/resources/simulations/__files/assessment-summary-M123456.json @@ -88,34 +88,32 @@ "sentencePlan": { "objectives": [ { - "objectiveCode": "Increased knowledge of physical/ psychological/ emotional self harm linked to drug use", - "objectiveMeasure": "RII", + "objectiveCodeDesc": "Increased knowledge of physical/ psychological/ emotional self harm linked to drug use", "objectiveSequence": 1, "criminogenicNeeds": [ { - "criminogenicNeed": "Risk to Public" + "criminogenicNeedDesc": "Risk to Public" } ], "actions": [ { - "action": "Drug counselling", + "actionDesc": "Drug counselling", "actionComment": "Frank will need to attend regularly" } ] }, { - "objectiveCode": "Improve employment related skills", - "objectiveMeasure": "RII", + "objectiveCodeDesc": "Improve employment related skills", "objectiveSequence": 2, "criminogenicNeeds": [ { - "criminogenicNeed": "EDUCATION TRAINING AND EMPLOYMENT" + "criminogenicNeedDesc": "Education Training and Employment" } ], "actions": [ { - "action": "Basic skills", - "actionComment": null + "actionDesc": "Basic skills", + "actionComment": "Some comment about their skills" } ] } diff --git a/projects/assessment-summary-and-delius/src/dev/resources/simulations/__files/assessment-summary-N123456.json b/projects/assessment-summary-and-delius/src/dev/resources/simulations/__files/assessment-summary-N123456.json index 6fc273a5f4..80df933e29 100644 --- a/projects/assessment-summary-and-delius/src/dev/resources/simulations/__files/assessment-summary-N123456.json +++ b/projects/assessment-summary-and-delius/src/dev/resources/simulations/__files/assessment-summary-N123456.json @@ -35,7 +35,7 @@ "riskStaffCustody": null, "riskStaffCommunity": null, "riskKnownAdultCustody": null, - "riskAdultCommunity": null, + "riskKnownAdultCommunity": null, "riskPublicCustody": null, "riskPublicCommunity": null, "riskChildrenCustody": null, diff --git a/projects/assessment-summary-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/oasys/OrdsClient.kt b/projects/assessment-summary-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/oasys/OrdsClient.kt index fd58cd6eb6..44f394d197 100644 --- a/projects/assessment-summary-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/oasys/OrdsClient.kt +++ b/projects/assessment-summary-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/oasys/OrdsClient.kt @@ -26,7 +26,7 @@ data class AssessmentSummary( val currentConcernsRiskOfSelfHarm: String? = null, val currentConcernsRiskOfSuicide: String? = null, val currentConcernsVulnerablity: String? = null, - val riskAdultCommunity: String? = null, + val riskKnownAdultCommunity: String? = null, val riskChildrenCommunity: String? = null, val riskChildrenCustody: String? = null, val riskKnownAdultCustody: String? = null, @@ -42,27 +42,35 @@ data class AssessmentSummary( val basicSentencePlan: String? = null, val sentencePlan: SentencePlan? = null ) { + /* + Order of Risk has to match Delius read order: + Children, Public, KnownAdult, Staff, Prisoner - community variant of each first + */ val riskFlags: List = listOf( - riskAdultCommunity, riskChildrenCommunity, riskChildrenCustody, - riskKnownAdultCustody, - riskPrisonersCustody, riskPublicCommunity, riskPublicCustody, + riskKnownAdultCommunity, + riskKnownAdultCustody, riskStaffCommunity, - riskStaffCustody + riskStaffCustody, + riskPrisonersCustody ).map { it?.firstOrNull()?.uppercase() ?: "N" } + /* + Order of Concerns has to match Delius read order: + Suicide, Harm, Custody, Hostel, Vulnerability, Abscond, Disruptive Behaviour, Breach of Trust + */ val concernFlags: List = listOf( - currentConcernsBreachOfTrust, + currentConcernsRiskOfSuicide, + currentConcernsRiskOfSelfHarm, currentConcernsCustody, - currentConcernsDisruptive, - currentConcernsEscape, currentConcernsHostel, - currentConcernsRiskOfSelfHarm, - currentConcernsRiskOfSuicide, - currentConcernsVulnerablity + currentConcernsVulnerablity, + currentConcernsEscape, + currentConcernsDisruptive, + currentConcernsBreachOfTrust ).map { when (it?.first()?.uppercase()) { "Y" -> "YES" @@ -114,19 +122,18 @@ data class SentencePlan( ) data class Objective( - val objectiveCode: String, - val objectiveMeasure: String, + val objectiveCodeDesc: String, val objectiveSequence: Long, val criminogenicNeeds: List, val actions: List ) data class Need( - val criminogenicNeed: String + val criminogenicNeedDesc: String ) data class Action( - val action: String, + val actionDesc: String, val actionComment: String? ) diff --git a/projects/assessment-summary-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/AssessmentService.kt b/projects/assessment-summary-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/AssessmentService.kt index 9537983fcd..1e8d94ec25 100644 --- a/projects/assessment-summary-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/AssessmentService.kt +++ b/projects/assessment-summary-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/AssessmentService.kt @@ -91,17 +91,18 @@ fun Objective.plan( person: Person, assessment: OasysAssessment ): SentencePlan { + fun Int.deliusIndex() = this + 1L val sp = SentencePlan( person, assessment, objectiveSequence, - objectiveCode + objectiveCodeDesc ) - criminogenicNeeds.forEachIndexed { index, need -> sp.withNeed(index.toLong(), need.criminogenicNeed) } + criminogenicNeeds.forEachIndexed { index, need -> sp.withNeed(index.deliusIndex(), need.criminogenicNeedDesc) } actions.forEachIndexed { index, action -> - sp.withWorkSummary(index.toLong(), action.action) + sp.withWorkSummary(index.deliusIndex(), action.actionDesc) action.actionComment?.also { - sp.withText(index.toLong(), it) + sp.withText(index.deliusIndex(), it) } } return sp diff --git a/projects/refer-and-monitor-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt b/projects/refer-and-monitor-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt index 90a98f19e0..8db21a6f5d 100644 --- a/projects/refer-and-monitor-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt +++ b/projects/refer-and-monitor-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt @@ -136,6 +136,8 @@ class DataLoader( mainCatRepository.save(SentenceGenerator.MAIN_CAT_F) + providerRepository.save(ProviderGenerator.NON_CRS_PROVIDER) + providerRepository.save(ProviderGenerator.INACTIVE_PROVIDER) val provider = providerRepository.saveAndFlush(ProviderGenerator.INTENDED_PROVIDER) pduRepository.saveAll( listOf( diff --git a/projects/refer-and-monitor-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/ProviderGenerator.kt b/projects/refer-and-monitor-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/ProviderGenerator.kt index ce1dc2561c..65aca6792b 100644 --- a/projects/refer-and-monitor-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/ProviderGenerator.kt +++ b/projects/refer-and-monitor-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/ProviderGenerator.kt @@ -12,6 +12,8 @@ import java.time.LocalDate object ProviderGenerator { val INTENDED_PROVIDER = generateProvider(Provider.INTENDED_PROVIDER_CODE, "Test Provider") + val NON_CRS_PROVIDER = generateProvider("N01", "Non-CRS Provider") + val INACTIVE_PROVIDER = generateProvider("N02", "Inactive Provider", endDate = LocalDate.of(2020, 1, 1)) val PROBATION_BOROUGH = generateBorough("PDU01") val PRISON_BOROUGH = generateBorough("PDU02") val PROBATION_DISTRICT = generateDistrict("LDU01", borough = PROBATION_BOROUGH) @@ -28,11 +30,17 @@ object ProviderGenerator { val LOCATIONS = listOf( generateLocation("TESTONE", buildingName = "Test One", streetName = "Mantle Place", postcode = "MP1 1PM"), generateLocation("TESTTWO", buildingName = "Test Two", postcode = "MP2 2PM", telephoneNumber = "020 123 6789"), - generateLocation("NOTCRS", providerId = 999L) + generateLocation("NOTCRS", provider = NON_CRS_PROVIDER), + generateLocation("DEFAULT", provider = INACTIVE_PROVIDER), // duplicate code linked to inactive provider + generateLocation("ENDDATE", endDate = LocalDate.of(2020, 1, 1)) ) - fun generateProvider(code: String, description: String, id: Long = IdGenerator.getAndIncrement()) = - Provider(code, description, id) + fun generateProvider( + code: String, + description: String, + endDate: LocalDate? = null, + id: Long = IdGenerator.getAndIncrement() + ) = Provider(code, description, endDate, id) fun generateBorough( code: String, @@ -83,7 +91,7 @@ object ProviderGenerator { telephoneNumber: String? = null, startDate: LocalDate = LocalDate.now().minusDays(7), endDate: LocalDate? = null, - providerId: Long = INTENDED_PROVIDER.id, + provider: Provider = INTENDED_PROVIDER, id: Long = IdGenerator.getAndIncrement() ) = Location( code, @@ -98,7 +106,7 @@ object ProviderGenerator { telephoneNumber, startDate, endDate, - providerId, + provider, id ) } diff --git a/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/provider/entity/Provider.kt b/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/provider/entity/Provider.kt index fab8401c6f..5af2e43860 100644 --- a/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/provider/entity/Provider.kt +++ b/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/provider/entity/Provider.kt @@ -18,6 +18,8 @@ class Provider( val description: String, + val endDate: LocalDate?, + @Id @Column(name = "probation_area_id") val id: Long @@ -73,8 +75,9 @@ class Location( val startDate: LocalDate, val endDate: LocalDate?, - @Column(name = "probation_area_id") - val providerId: Long, + @ManyToOne + @JoinColumn(name = "probation_area_id") + val provider: Provider, @Id @Column(name = "office_location_id") @@ -130,17 +133,28 @@ interface TeamRepository : JpaRepository { fun TeamRepository.getByCode(code: String) = findByCode(code) ?: throw NotFoundException("Team", "code", code) interface LocationRepository : JpaRepository { - fun findByCodeAndEndDateIsNull(code: String): Location? + @Query( + """ + select l from Location l + join fetch l.provider p + where l.code = :code + and (p.endDate is null or p.endDate > current_date) + and (l.endDate is null or l.endDate > current_date) + """ + ) + fun findActiveLocationByCode(code: String): Location? @Query( """ select l from Location l - where l.providerId = :providerId - and l.endDate is null + join fetch l.provider p + where p.id = :providerId + and (p.endDate is null or p.endDate > current_date) + and (l.endDate is null or l.endDate > current_date) """ ) fun findAllLocationsForProvider(providerId: Long): List } fun LocationRepository.getByCode(code: String) = - findByCodeAndEndDateIsNull(code) ?: throw NotFoundException("Location", "code", code) + findActiveLocationByCode(code) ?: throw NotFoundException("Location", "code", code) diff --git a/settings.gradle.kts b/settings.gradle.kts index f6d577ec1a..006e8e0648 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -70,7 +70,7 @@ dependencyResolutionManagement { library("mockito-kotlin", "org.mockito.kotlin:mockito-kotlin:5.2.1") library("mockito-inline", "org.mockito:mockito-inline:5.2.0") bundle("mockito", listOf("mockito-kotlin", "mockito-inline")) - library("insights", "com.microsoft.azure:applicationinsights-web:3.4.18") + library("insights", "com.microsoft.azure:applicationinsights-web:3.4.19") library("sentry", "io.sentry:sentry-spring-boot-starter-jakarta:7.1.0") library( "opentelemetry-annotations",