diff --git a/openapi.yml b/openapi.yml index 9351725e3..f96d9a5b1 100644 --- a/openapi.yml +++ b/openapi.yml @@ -497,7 +497,7 @@ paths: get: tags: - risks - summary: Returns risk scores from the last year associated with a person, sorted by completedDate (newest first). + summary: Returns risk scores from the last year associated with a person, sorted by completedDate (newest first). This endpoint does not serve LAO (Limited Access Offender) data. parameters: - $ref: "#/components/parameters/hmppsId" - $ref: "#/components/parameters/page" @@ -649,7 +649,7 @@ paths: tags: - needs summary: > - Returns criminogenic needs associated with a person. + Returns criminogenic needs associated with a person. This endpoint does not serve LAO (Limited Access Offender) data. Note: Criminogenic needs are dynamic factors that are directly linked to criminal behaviour. Eight criminogenic needs are measured in OASys: Accommodation, Employability, Relationships, Lifestyle and Associates, Drug Misuse, Alcohol Misuse, Thinking & Behaviour and Attitudes. These are scored according to whether there is “no need”, “some need” or “severe need”, and a need is identified in a specific section based on calculations around these scores. However, the process by which needs are assessed is changing as early as next year (2024), specifically moving to a strength-based model that seeks to identify and develop the strengths of people with convictions. As a consequence of this, the information provided by this endpoint will also change. @@ -688,7 +688,7 @@ paths: get: tags: - risks - summary: Returns Risk of Serious Harm (ROSH) risks associated with a person. Returns only assessments completed in the last year. + summary: Returns Risk of Serious Harm (ROSH) risks associated with a person. Returns only assessments completed in the last year. This endpoint does not serve LAO (Limited Access Offender) data. parameters: - $ref: "#/components/parameters/hmppsId" responses: diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/extensions/WebClientWrapper.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/extensions/WebClientWrapper.kt index df565beaf..3a582369a 100644 --- a/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/extensions/WebClientWrapper.kt +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/extensions/WebClientWrapper.kt @@ -38,6 +38,7 @@ class WebClientWrapper( headers: Map<String, String>, upstreamApi: UpstreamApi, requestBody: Map<String, Any?>? = null, + forbiddenAsError: Boolean = false, ): WebClientWrapperResponse<T> { return try { val responseData = @@ -47,7 +48,7 @@ class WebClientWrapper( WebClientWrapperResponse.Success(responseData) } catch (exception: WebClientResponseException) { - getErrorType(exception, upstreamApi) + getErrorType(exception, upstreamApi, forbiddenAsError) } } @@ -57,6 +58,7 @@ class WebClientWrapper( headers: Map<String, String>, upstreamApi: UpstreamApi, requestBody: Map<String, Any?>? = null, + forbiddenAsError: Boolean = false, ): WebClientWrapperResponse<List<T>> { return try { val responseData = @@ -67,7 +69,7 @@ class WebClientWrapper( WebClientWrapperResponse.Success(responseData) } catch (exception: WebClientResponseException) { - getErrorType(exception, upstreamApi) + getErrorType(exception, upstreamApi, forbiddenAsError) } } @@ -92,10 +94,12 @@ class WebClientWrapper( fun getErrorType( exception: WebClientResponseException, upstreamApi: UpstreamApi, + forbiddenAsError: Boolean = false, ): WebClientWrapperResponse.Error { val errorType = when (exception.statusCode) { HttpStatus.NOT_FOUND -> UpstreamApiError.Type.ENTITY_NOT_FOUND + HttpStatus.FORBIDDEN -> if (forbiddenAsError) UpstreamApiError.Type.FORBIDDEN else throw exception else -> throw exception } return WebClientWrapperResponse.Error( diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/gateways/AssessRisksAndNeedsGateway.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/gateways/AssessRisksAndNeedsGateway.kt index 8d96d8c7e..f566bf665 100644 --- a/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/gateways/AssessRisksAndNeedsGateway.kt +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/gateways/AssessRisksAndNeedsGateway.kt @@ -31,6 +31,7 @@ class AssessRisksAndNeedsGateway( "/risks/crn/$id/predictors/all", authenticationHeader(), UpstreamApi.ASSESS_RISKS_AND_NEEDS, + forbiddenAsError = true, ) return when (result) { @@ -59,6 +60,7 @@ class AssessRisksAndNeedsGateway( "/risks/crn/$id", authenticationHeader(), UpstreamApi.ASSESS_RISKS_AND_NEEDS, + forbiddenAsError = true, ) return when (result) { @@ -82,6 +84,7 @@ class AssessRisksAndNeedsGateway( "/needs/crn/$id", authenticationHeader(), UpstreamApi.ASSESS_RISKS_AND_NEEDS, + forbiddenAsError = true, ) return when (result) { diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/gateways/assessrisksandneeds/GetRiskPredictorScoresForPersonTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/gateways/assessrisksandneeds/GetRiskPredictorScoresForPersonTest.kt index e16580a16..09618a1bc 100644 --- a/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/gateways/assessrisksandneeds/GetRiskPredictorScoresForPersonTest.kt +++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/gateways/assessrisksandneeds/GetRiskPredictorScoresForPersonTest.kt @@ -116,5 +116,13 @@ class GetRiskPredictorScoresForPersonTest( response.hasError(UpstreamApiError.Type.ENTITY_NOT_FOUND).shouldBeTrue() } + + it("returns a 403 FORBIDDEN status code when forbidden") { + assessRisksAndNeedsApiMockServer.stubGetRiskPredictorScoresForPerson(deliusCrn, "", HttpStatus.FORBIDDEN) + + val response = assessRisksAndNeedsGateway.getRiskPredictorScoresForPerson(deliusCrn) + + response.hasError(UpstreamApiError.Type.FORBIDDEN).shouldBeTrue() + } }, ) diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetNeedsForPersonServiceTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetNeedsForPersonServiceTest.kt index be6194c10..eaae73484 100644 --- a/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetNeedsForPersonServiceTest.kt +++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetNeedsForPersonServiceTest.kt @@ -110,12 +110,18 @@ internal class GetNeedsForPersonServiceTest( ) } - it("records upstream API error for probation offender search") { + it("records upstream 404 API error for probation offender search") { val response = getNeedsForPersonService.execute(hmppsId) response.hasErrorCausedBy(UpstreamApiError.Type.ENTITY_NOT_FOUND, UpstreamApi.PROBATION_OFFENDER_SEARCH).shouldBe(true) } + it("records upstream 403 API error for probation offender search") { + val response = getNeedsForPersonService.execute(hmppsId) + + response.hasErrorCausedBy(UpstreamApiError.Type.FORBIDDEN, UpstreamApi.ASSESS_RISKS_AND_NEEDS).shouldBe(true) + } + it("does not get needs from ARN") { getNeedsForPersonService.execute(hmppsId) diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetRiskPredictorScoresForPersonServiceTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetRiskPredictorScoresForPersonServiceTest.kt index 76e3ce191..5b8454c6f 100644 --- a/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetRiskPredictorScoresForPersonServiceTest.kt +++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetRiskPredictorScoresForPersonServiceTest.kt @@ -110,12 +110,20 @@ internal class GetRiskPredictorScoresForPersonServiceTest( ) } - it("records upstream API error for probation offender search") { + it("records upstream 404 API error for probation offender search") { val response = getRiskPredictorScoresForPersonService.execute(hmppsId) response.hasErrorCausedBy(UpstreamApiError.Type.ENTITY_NOT_FOUND, UpstreamApi.PROBATION_OFFENDER_SEARCH).shouldBe(true) } + it("records upstream 403 API error for probation offender search") { + val response = getRiskPredictorScoresForPersonService.execute(hmppsId) + + response.data.isEmpty() + response.errors.shouldHaveSize(1) + response.hasErrorCausedBy(UpstreamApiError.Type.FORBIDDEN, UpstreamApi.ASSESS_RISKS_AND_NEEDS).shouldBe(true) + } + it("does not get risk predictor scores from ARN") { getRiskPredictorScoresForPersonService.execute(hmppsId) diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetRiskSeriousHarmForPersonServiceTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetRiskSeriousHarmForPersonServiceTest.kt index 3105112ca..868f4cefa 100644 --- a/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetRiskSeriousHarmForPersonServiceTest.kt +++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetRiskSeriousHarmForPersonServiceTest.kt @@ -2,6 +2,7 @@ package uk.gov.justice.digital.hmpps.hmppsintegrationapi.services import io.kotest.core.spec.style.DescribeSpec import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.shouldBe import org.mockito.Mockito import org.mockito.internal.verification.VerificationModeFactory @@ -90,12 +91,20 @@ GetRiskSeriousHarmForPersonServiceTest( ) } - it("records upstream API error for probation offender search") { + it("records upstream 404 API error for probation offender search") { val response = getRiskSeriousHarmForPersonService.execute(hmppsId) response.hasErrorCausedBy(UpstreamApiError.Type.ENTITY_NOT_FOUND, UpstreamApi.PROBATION_OFFENDER_SEARCH).shouldBe(true) } + it("records upstream 403 API error for probation offender search") { + val response = getRiskSeriousHarmForPersonService.execute(hmppsId) + + response.data.shouldBeNull() + response.errors.shouldHaveSize(1) + response.hasErrorCausedBy(UpstreamApiError.Type.FORBIDDEN, UpstreamApi.ASSESS_RISKS_AND_NEEDS).shouldBe(true) + } + it("does not get risks from ARN") { getRiskSeriousHarmForPersonService.execute(hmppsId)