diff --git a/Dockerfile.setup-case-notes-api b/Dockerfile.setup-case-notes-api new file mode 100644 index 000000000..153bde133 --- /dev/null +++ b/Dockerfile.setup-case-notes-api @@ -0,0 +1,9 @@ +FROM node:current-alpine3.17 + +RUN apk update && apk add bash + +COPY src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/prismMocks/case-notes-api-docs.json /case-notes-api-docs.json + +RUN npm install -g @stoplight/prism-cli + +CMD prism mock -p 4010 -h 0.0.0.0 /case-notes-api-docs.json diff --git a/docker-compose.yml b/docker-compose.yml index 9cb91f349..6cd6e600e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -114,6 +114,16 @@ services: ports: - '4070:4010' + case-notes-api: + build: + context: . + dockerfile: Dockerfile.setup-case-notes-api + container_name: case-notes-api + healthcheck: + test: 'wget --header="Authorization: Bearer abc" http://0.0.0.0:4010/case-notes/1234 -O /dev/null' + ports: + - '4080:4010' + local-stack-aws: image: localstack/localstack:0.14.0 container_name: local-stack-aws diff --git a/openapi.yml b/openapi.yml index fa900bbd6..00b37dee6 100644 --- a/openapi.yml +++ b/openapi.yml @@ -516,6 +516,36 @@ paths: PersonNotFoundError: $ref: "#/components/examples/PersonNotFoundError" + /v1/persons/{HmppsId}/case-notes: + get: + summary: Returns case notes associated with a person. + parameters: + - $ref: "#/components/parameters/HmppsId" + responses: + "200": + description: Successfully found case notes for a person with the provided HMPPS ID. + content: + application/json: + schema: + type: object + properties: + data: + type: array + minItems: 0 + items: + $ref: "#/components/schemas/CaseNote" + pagination: + $ref: "#/components/schemas/Pagination" + "404": + description: Failed to find case notes for a person with the provided HMPPS ID. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + PersonNotFoundError: + $ref: "#/components/examples/PersonNotFoundError" + /{TBC}/v1/persons/{Id}/accommodations: get: summary: FUTURE ENDPOINT - Returns accommodation and referral information associated with a person. @@ -806,6 +836,12 @@ components: ageAtRelease: type: integer format: int64 + CaseNote: + type: object + properties: + caseNoteId: + type: string + example: 1234 CaseSentence: required: - date diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/controllers/v1/person/CaseNotesController.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/controllers/v1/person/CaseNotesController.kt new file mode 100755 index 000000000..98aebd34c --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/controllers/v1/person/CaseNotesController.kt @@ -0,0 +1,40 @@ +package uk.gov.justice.digital.hmpps.hmppsintegrationapi.controllers.v1.person + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.exception.EntityNotFoundException +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.extensions.decodeUrlCharacters +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.CaseNote +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.UpstreamApi +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.UpstreamApiError +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.services.GetCaseNotesForPersonService +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.services.internal.AuditService +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.util.PaginatedResponse +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.util.paginateWith + +@RestController +@RequestMapping("/v1/persons") +class CaseNotesController( + @Autowired val getCaseNoteForPersonService: GetCaseNotesForPersonService, + @Autowired val auditService: AuditService, +) { + @GetMapping("{encodedHmppsId}/case-notes") + fun getCaseNotesForPerson( + @PathVariable encodedHmppsId: String, + @RequestParam(required = false, defaultValue = "1", name = "page") page: Int, + @RequestParam(required = false, defaultValue = "10", name = "perPage") perPage: Int, + ): PaginatedResponse { + val hmppsId = encodedHmppsId.decodeUrlCharacters() + val response = getCaseNoteForPersonService.execute(hmppsId) + + if (response.hasErrorCausedBy(UpstreamApiError.Type.ENTITY_NOT_FOUND, causedBy = UpstreamApi.CASE_NOTES)) { + throw EntityNotFoundException("Could not find person with id: $hmppsId") + } + auditService.createEvent("GET_CASE_NOTES", "Person case notes with hmpps id: $hmppsId has been retrieved") + return response.data.paginateWith(page, perPage) + } +} diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/gateways/CaseNotesGateway.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/gateways/CaseNotesGateway.kt new file mode 100644 index 000000000..8cc813a16 --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/gateways/CaseNotesGateway.kt @@ -0,0 +1,52 @@ +package uk.gov.justice.digital.hmpps.hmppsintegrationapi.gateways + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value +import org.springframework.http.HttpMethod +import org.springframework.stereotype.Component +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.extensions.WebClientWrapper +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.CaseNote +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Response +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.UpstreamApi +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.nomis.NomisPageCaseNote + +@Component +class CaseNotesGateway( + @Value("\${services.case-notes.base-url}") baseUrl: String, +) { + private val webClient = WebClientWrapper(baseUrl) + + @Autowired + lateinit var hmppsAuthGateway: HmppsAuthGateway + + fun getCaseNotesForPerson(id: String): Response> { + val result = + webClient.request( + HttpMethod.GET, + "/case-notes/$id", + authenticationHeader(), + UpstreamApi.CASE_NOTES, + ) + + return when (result) { + is WebClientWrapper.WebClientWrapperResponse.Success -> { + Response(data = result.data.toCaseNotes()) + } + + is WebClientWrapper.WebClientWrapperResponse.Error -> { + Response( + data = emptyList(), + errors = result.errors, + ) + } + } + } + + private fun authenticationHeader(): Map { + val token = hmppsAuthGateway.getClientToken("CaseNotes") + + return mapOf( + "Authorization" to "Bearer $token", + ) + } +} diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/models/hmpps/CaseNote.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/models/hmpps/CaseNote.kt new file mode 100644 index 000000000..a4c6c117a --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/models/hmpps/CaseNote.kt @@ -0,0 +1,4 @@ +package uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps +data class CaseNote( + val caseNoteId: String? = null, +) diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/models/hmpps/UpstreamApi.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/models/hmpps/UpstreamApi.kt index 1dfc669a8..a31fce3a9 100644 --- a/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/models/hmpps/UpstreamApi.kt +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/models/hmpps/UpstreamApi.kt @@ -1,5 +1,5 @@ package uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps enum class UpstreamApi { - NOMIS, PRISONER_OFFENDER_SEARCH, PROBATION_OFFENDER_SEARCH, NDELIUS, ASSESS_RISKS_AND_NEEDS, ADJUDICATIONS, CVL, TEST + NOMIS, PRISONER_OFFENDER_SEARCH, PROBATION_OFFENDER_SEARCH, NDELIUS, ASSESS_RISKS_AND_NEEDS, ADJUDICATIONS, CVL, CASE_NOTES, TEST } diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/models/nomis/NomisCaseNote.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/models/nomis/NomisCaseNote.kt new file mode 100644 index 000000000..8acdf29ca --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/models/nomis/NomisCaseNote.kt @@ -0,0 +1,5 @@ +package uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.nomis + +data class NomisCaseNote( + val caseNoteId: String, +) diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/models/nomis/NomisPageCaseNote.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/models/nomis/NomisPageCaseNote.kt new file mode 100644 index 000000000..18a974182 --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/models/nomis/NomisPageCaseNote.kt @@ -0,0 +1,11 @@ +package uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.nomis + +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.CaseNote + +data class NomisPageCaseNote( + val content: List = listOf(), +) { + fun toCaseNotes(): List = this.content.map { + CaseNote(caseNoteId = it.caseNoteId) + } +} diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/prismMocks/case-notes-api-docs.json b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/prismMocks/case-notes-api-docs.json new file mode 100644 index 000000000..44f81a53e --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/prismMocks/case-notes-api-docs.json @@ -0,0 +1 @@ +{"openapi":"3.0.1","info":{"title":"HMPPS Offender Case Notes","description":"HMPPS Offender Case Notes API","contact":{"name":"HMPPS Digital Studio","email":"feedback@digital.justice.gov.uk"},"version":"2024-02-06.5013.89d6695"},"servers":[{"url":"https://offender-case-notes.service.justice.gov.uk","description":"Prod"},{"url":"https://preprod.offender-case-notes.service.justice.gov.uk","description":"PreProd"},{"url":"https://dev.offender-case-notes.service.justice.gov.uk","description":"Development"},{"url":"http://localhost:8080","description":"Local"}],"tags":[{"name":"publish-notes","description":"Prison Notes Controller"},{"name":"case-notes","description":"Case Note Controller"}],"paths":{"/queue-admin/retry-dlq/{dlqName}":{"put":{"tags":["hmpps-queue-resource"],"operationId":"retryDlq","parameters":[{"name":"dlqName","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RetryDlqResult"}}}}}}},"/queue-admin/retry-all-dlqs":{"put":{"tags":["hmpps-queue-resource"],"operationId":"retryAllDlqs","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RetryDlqResult"}}}}}}}},"/queue-admin/purge-queue/{queueName}":{"put":{"tags":["hmpps-queue-resource"],"operationId":"purgeQueue","parameters":[{"name":"queueName","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PurgeQueueResult"}}}}}}},"/case-notes/{offenderIdentifier}/{caseNoteIdentifier}":{"get":{"tags":["case-notes"],"summary":"Retrieves a case note","operationId":"getCaseNote","parameters":[{"name":"offenderIdentifier","in":"path","description":"Offender Identifier","required":true,"schema":{"type":"string"},"example":"A1234AA"},{"name":"caseNoteIdentifier","in":"path","description":"Case Note Id","required":true,"schema":{"type":"string"},"example":"518b2200-6489-4c77-8514-10cf80ccd488"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaseNote"}}}},"404":{"description":"Offender or case note not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"put":{"tags":["case-notes"],"summary":"Amend Case Note for offender","description":"Amend a case note information adds and additional entry to the note","operationId":"amendCaseNote","parameters":[{"name":"offenderIdentifier","in":"path","description":"Offender Identifier","required":true,"schema":{"type":"string"},"example":"A1234AA"},{"name":"caseNoteIdentifier","in":"path","description":"Case Note Id","required":true,"schema":{"type":"string"},"example":"518b2200-6489-4c77-8514-10cf80ccd488"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCaseNote"}}},"required":true},"responses":{"404":{"description":"No case notes where found for this offender and case note id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"201":{"description":"The Case Note has been recorded. The updated object is returned including the status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaseNote"}}}}}}},"/case-notes/types/{parentType}":{"put":{"tags":["case-notes"],"summary":"Update Case Note Type","description":"Creates a new case note type","operationId":"updateCaseNoteType","parameters":[{"name":"parentType","in":"path","description":"Parent Case Note Type","required":true,"schema":{"type":"string"},"example":"OBS"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCaseNoteType"}}},"required":true},"responses":{"404":{"description":"The case note type is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"200":{"description":"The case note type has been updated. The updated object is returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaseNoteTypeDto"}}}}}},"post":{"tags":["case-notes"],"summary":"Add New Case Note Sub Type","description":"Creates a new case note sub type","operationId":"createCaseNoteSubType","parameters":[{"name":"parentType","in":"path","description":"Parent Case Note Type","required":true,"schema":{"type":"string"},"example":"GEN"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewCaseNoteType"}}},"required":true},"responses":{"201":{"description":"The Case Note Sub Type has been recorded. The updated object is returned including the status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaseNoteTypeDto"}}}},"409":{"description":"The case note sub type has already been recorded. The current unmodified object (including status) is returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/case-notes/types/{parentType}/{subType}":{"put":{"tags":["case-notes"],"summary":"Update Case Note Sub Type","description":"Creates a new case note sub type","operationId":"updateCaseNoteSubType","parameters":[{"name":"parentType","in":"path","description":"Parent Case Note Type","required":true,"schema":{"type":"string"},"example":"OBS"},{"name":"subType","in":"path","description":"Sub Case Note Type","required":true,"schema":{"type":"string"},"example":"GEN"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCaseNoteType"}}},"required":true},"responses":{"404":{"description":"The case note sub type is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"200":{"description":"The case note sub type update has been updated. The updated object is returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaseNoteTypeDto"}}}}}}},"/publish-notes":{"post":{"tags":["publish-notes"],"summary":"Publish sensitive case notes","operationId":"publishCaseNotes","parameters":[{"name":"fromDateTime","in":"query","description":"A timestamp that indicates the earliest record required","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"toDateTime","in":"query","description":"A timestamp that indicates the latest record required","required":true,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Number of notes to be published (asynchronously)","content":{"application/json":{"schema":{"type":"integer","format":"int32"}}}}}}},"/case-notes/{offenderIdentifier}":{"get":{"tags":["case-notes"],"summary":"Retrieves a list of case notes","operationId":"getCaseNotes","parameters":[{"name":"offenderIdentifier","in":"path","description":"Offender Identifier","required":true,"schema":{"type":"string"},"example":"A1234AA"},{"name":"filter","in":"query","description":"Optionally specify a case note filter","required":true,"schema":{"$ref":"#/components/schemas/CaseNoteFilter"}},{"name":"pageable","in":"query","required":true,"schema":{"$ref":"#/components/schemas/Pageable"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageCaseNote"}}}},"404":{"description":"Offender not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["case-notes"],"summary":"Add Case Note for offender","description":"Creates a note for a specific type/subType","operationId":"createCaseNote","parameters":[{"name":"offenderIdentifier","in":"path","description":"Offender Identifier","required":true,"schema":{"type":"string"},"example":"A1234AA"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewCaseNote"}}},"required":true},"responses":{"201":{"description":"The Case Note has been recorded. The updated object is returned including the status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaseNote"}}}},"409":{"description":"The case note has already been recorded under the booking. The current unmodified object (including status) is returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/case-notes/types":{"get":{"tags":["case-notes"],"summary":"Retrieves a list of case note types","operationId":"getCaseNoteTypes","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CaseNoteTypeDto"}}}}},"404":{"description":"Case notes types not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["case-notes"],"summary":"Add New Case Note Type","description":"Creates a new case note type","operationId":"createCaseNoteType","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewCaseNoteType"}}},"required":true},"responses":{"409":{"description":"The case note type has already been recorded. The current unmodified object (including status) is returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"201":{"description":"The Case Note Type has been recorded. The updated object is returned including the status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaseNoteTypeDto"}}}}}}},"/queue-admin/get-dlq-messages/{dlqName}":{"get":{"tags":["hmpps-queue-resource"],"operationId":"getDlqMessages","parameters":[{"name":"dlqName","in":"path","required":true,"schema":{"type":"string"}},{"name":"maxMessages","in":"query","required":false,"schema":{"type":"integer","format":"int32","default":100}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/GetDlqResult"}}}}}}},"/case-notes/types-for-user":{"get":{"tags":["case-notes"],"summary":"Retrieves a list of case note types for this user","operationId":"getUserCaseNoteTypes","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CaseNoteTypeDto"}}}}},"404":{"description":"Case notes types not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/case-notes/{offenderIdentifier}/{caseNoteId}":{"delete":{"tags":["case-notes"],"summary":"Deletes a case note","operationId":"softDeleteCaseNote","parameters":[{"name":"offenderIdentifier","in":"path","description":"Offender Identifier","required":true,"schema":{"type":"string"},"example":"A1234AA"},{"name":"caseNoteId","in":"path","description":"Case Note Id","required":true,"schema":{"type":"string"},"example":"518b2200-6489-4c77-8514-10cf80ccd488"}],"responses":{"200":{"description":"OK"},"404":{"description":"Offender or case note not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/case-notes/amendment/{offenderIdentifier}/{caseNoteAmendmentId}":{"delete":{"tags":["case-notes"],"summary":"Deletes a case note amendment","operationId":"softDeleteCaseNoteAmendment","parameters":[{"name":"offenderIdentifier","in":"path","description":"Offender Identifier","required":true,"schema":{"type":"string"},"example":"A1234AA"},{"name":"caseNoteAmendmentId","in":"path","description":"Case Note Amendment Id","required":true,"schema":{"type":"integer","format":"int64"},"example":1}],"responses":{"200":{"description":"OK"},"404":{"description":"Offender or case note not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"schemas":{"DlqMessage":{"required":["body","messageId"],"type":"object","properties":{"body":{"type":"object","additionalProperties":{"type":"object"}},"messageId":{"type":"string"}}},"RetryDlqResult":{"required":["messages","messagesFoundCount"],"type":"object","properties":{"messagesFoundCount":{"type":"integer","format":"int32"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/DlqMessage"}}}},"PurgeQueueResult":{"required":["messagesFoundCount"],"type":"object","properties":{"messagesFoundCount":{"type":"integer","format":"int32"}}},"UpdateCaseNote":{"required":["text"],"type":"object","properties":{"text":{"type":"string","description":"Text of case note","example":"This is a case note message"}},"description":"Amend a Case Note"},"ErrorResponse":{"required":["status"],"type":"object","properties":{"status":{"type":"integer","format":"int32"},"errorCode":{"type":"integer","format":"int32"},"userMessage":{"type":"string"},"developerMessage":{"type":"string"},"moreInfo":{"type":"string"}}},"CaseNote":{"required":["amendments","authorName","authorUserId","caseNoteId","creationDateTime","eventId","occurrenceDateTime","offenderIdentifier","sensitive","source","subType","subTypeDescription","text","type","typeDescription"],"type":"object","properties":{"caseNoteId":{"type":"string","description":"Case Note Id (unique)","example":"12311312"},"offenderIdentifier":{"type":"string","description":"Offender Unique Identifier","example":"A1234AA"},"type":{"type":"string","description":"Case Note Type","example":"KA"},"typeDescription":{"type":"string","description":"Case Note Type Description","example":"Key Worker"},"subType":{"type":"string","description":"Case Note Sub Type","example":"KS"},"subTypeDescription":{"type":"string","description":"Case Note Sub Type Description","example":"Key Worker Session"},"source":{"type":"string","description":"Source Type","example":"INST"},"creationDateTime":{"type":"string","description":"Date and Time of Case Note creation","format":"date-time"},"occurrenceDateTime":{"type":"string","description":"Date and Time of when case note contact with offender was made","format":"date-time"},"authorName":{"type":"string","description":"Full name of case note author","example":"John Smith"},"authorUserId":{"type":"string","description":"User Id of case note author - staffId for nomis users, userId for auth users","example":"12345"},"text":{"type":"string","description":"Case Note Text","example":"This is some text"},"locationId":{"type":"string","description":"Location Id representing where Case Note was made.","example":"MDI"},"eventId":{"type":"integer","description":"Delius number representation of the case note id - will be negative for sensitive case note types","format":"int32","example":-23},"sensitive":{"type":"boolean","description":"Sensitive Note","example":true},"amendments":{"type":"array","description":"Ordered list of amendments to the case note (oldest first)","items":{"$ref":"#/components/schemas/CaseNoteAmendment"}}},"description":"Case Note"},"CaseNoteAmendment":{"required":["additionalNoteText","authorName","authorUserId","authorUserName","caseNoteAmendmentId","creationDateTime"],"type":"object","properties":{"caseNoteAmendmentId":{"type":"integer","description":"Amendment Case Note Id (unique)","format":"int64","example":123232},"creationDateTime":{"type":"string","description":"Date and Time of Case Note creation","format":"date-time"},"authorUserName":{"type":"string","description":"Username of the user amending the case note","example":"USER1"},"authorName":{"type":"string","description":"Name of the user amending the case note","example":"Mickey Mouse"},"authorUserId":{"type":"string","description":"User Id of the user amending the case note - staffId for nomis users, userId for auth users","example":"12345"},"additionalNoteText":{"type":"string","description":"Additional Case Note Information","example":"Some Additional Text"}},"description":"Case Note Amendment"},"UpdateCaseNoteType":{"required":["description"],"type":"object","properties":{"description":{"type":"string","description":"Type Description","example":"General Note Type"},"active":{"type":"boolean","description":"Active Type","example":true},"sensitive":{"type":"boolean","description":"Sensitive Case Note Type, default true","example":true},"restrictedUse":{"type":"boolean","description":"Restricted Use, default true","example":true}},"description":"Update a Case Note Type"},"CaseNoteTypeDto":{"required":["activeFlag","code","description"],"type":"object","properties":{"code":{"type":"string","description":"Case Note Code","example":"OBSERVE"},"description":{"type":"string","description":"Case Note description.","example":"Observations"},"activeFlag":{"type":"string","description":"Active indicator flag.","example":"Y","enum":["Y,N"]},"subCodes":{"type":"array","description":"List of case note sub types","nullable":true,"items":{"$ref":"#/components/schemas/CaseNoteTypeDto"}},"source":{"type":"string","description":"Source of Case Note Type, legacy case note are null","example":"OCNS"},"sensitive":{"type":"boolean","description":"Indicates the type of note is sensitive","example":true},"restrictedUse":{"type":"boolean","description":"Indicates the type of note can only be created by a sub-set of users (e.g. POMs)","example":true}},"description":"Case Note Type"},"NewCaseNote":{"required":["locationId","occurrenceDateTime","subType","text","type"],"type":"object","properties":{"locationId":{"type":"string","description":"Location where case note was made, if blank it will be looked up in Nomis","example":"MDI"},"type":{"type":"string","description":"Type of case note","example":"GEN"},"subType":{"type":"string","description":"Sub Type of case note","example":"OBS"},"occurrenceDateTime":{"type":"string","description":"Occurrence time of case note","format":"date-time"},"text":{"type":"string","description":"Text of case note","example":"This is a case note message"}},"description":"Create a Case Note"},"NewCaseNoteType":{"required":["description","type"],"type":"object","properties":{"type":{"type":"string","description":"Type of case note","example":"GEN"},"description":{"type":"string","description":"Type Description","example":"General Note Type"},"active":{"type":"boolean","description":"Active Type, default true","example":true},"sensitive":{"type":"boolean","description":"Sensitive Case Note Type, default true","example":true},"restrictedUse":{"type":"boolean","description":"Restricted Use, default true","example":true}},"description":"Create a New Case Note Type"},"GetDlqResult":{"required":["messages","messagesFoundCount","messagesReturnedCount"],"type":"object","properties":{"messagesFoundCount":{"type":"integer","format":"int32"},"messagesReturnedCount":{"type":"integer","format":"int32"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/DlqMessage"}}}},"CaseNoteFilter":{"type":"object","properties":{"type":{"type":"string","description":"Filter by Case Note Type","example":"KA"},"subType":{"type":"string","description":"Filter by Case Note Sub Type","example":"KS"},"startDate":{"type":"string","description":"Filter case notes from this date","format":"date-time"},"endDate":{"type":"string","description":"Filter case notes up to this date","format":"date-time"},"locationId":{"type":"string","description":"Filter by the location","example":"MDI"},"authorUsername":{"type":"string","description":"Filter by username","example":"USER1"}},"description":"Case Note Filter"},"Pageable":{"type":"object","properties":{"page":{"minimum":0,"type":"integer","format":"int32"},"size":{"minimum":1,"type":"integer","format":"int32"},"sort":{"type":"array","items":{"type":"string"}}}},"PageCaseNote":{"type":"object","properties":{"totalElements":{"type":"integer","format":"int64"},"totalPages":{"type":"integer","format":"int32"},"first":{"type":"boolean"},"last":{"type":"boolean"},"size":{"type":"integer","format":"int32"},"content":{"type":"array","items":{"$ref":"#/components/schemas/CaseNote"}},"number":{"type":"integer","format":"int32"},"sort":{"$ref":"#/components/schemas/SortObject"},"numberOfElements":{"type":"integer","format":"int32"},"pageable":{"$ref":"#/components/schemas/PageableObject"},"empty":{"type":"boolean"}}},"PageableObject":{"type":"object","properties":{"offset":{"type":"integer","format":"int64"},"sort":{"$ref":"#/components/schemas/SortObject"},"pageSize":{"type":"integer","format":"int32"},"unpaged":{"type":"boolean"},"pageNumber":{"type":"integer","format":"int32"},"paged":{"type":"boolean"}}},"SortObject":{"type":"object","properties":{"empty":{"type":"boolean"},"sorted":{"type":"boolean"},"unsorted":{"type":"boolean"}}}}}} diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetCaseNotesForPersonService.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetCaseNotesForPersonService.kt new file mode 100755 index 000000000..eb79f26ee --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetCaseNotesForPersonService.kt @@ -0,0 +1,29 @@ +package uk.gov.justice.digital.hmpps.hmppsintegrationapi.services + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Service +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.gateways.CaseNotesGateway +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.CaseNote +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Response + +@Service +class GetCaseNotesForPersonService( + @Autowired val caseNotesGateway: CaseNotesGateway, + @Autowired val getPersonService: GetPersonService, +) { + fun execute(hmppsId: String): Response> { + val personResponse = getPersonService.execute(hmppsId = hmppsId) + val nomisNumber = personResponse.data?.identifiers?.nomisNumber + + var caseNotes: Response> = Response(data = emptyList()) + + if (nomisNumber != null) { + caseNotes = caseNotesGateway.getCaseNotesForPerson(id = nomisNumber) + } + + return Response( + data = caseNotes.data, + errors = personResponse.errors + caseNotes.errors, + ) + } +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index ef62a24b5..a2040c335 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -15,6 +15,8 @@ services: base-url: https://manage-adjudications-api-dev.hmpps.service.justice.gov.uk create-and-vary-licence: base-url: https://create-and-vary-a-licence-api-dev.hmpps.service.justice.gov.uk + case-notes: + base-url: https://dev.offender-case-notes.service.justice.gov.uk hmpps-auth: base-url: https://sign-in-dev.hmpps.service.justice.gov.uk username: ${CLIENT_ID} @@ -37,6 +39,7 @@ authorisation: - "/v1/persons/.*/risks" - "/v1/persons/.*/adjudications" - "/v1/persons/.*/licences/conditions" + - "/v1/persons/.*/case-notes" ctrlo: - "/v1/epf/person-details/.*/.*" kubernetes-health-check-client: diff --git a/src/main/resources/application-local-docker.yml b/src/main/resources/application-local-docker.yml index f45b4c6e8..802cbfdbd 100644 --- a/src/main/resources/application-local-docker.yml +++ b/src/main/resources/application-local-docker.yml @@ -15,6 +15,8 @@ services: base-url: http://adjudications-api:4010 create-and-vary-licence: base-url: http://create-and-vary-licence-api:4010 + case-notes: + base-url: http://case-notes-api:4010 hmpps-auth: base-url: http://hmpps-auth:8080 @@ -34,6 +36,7 @@ authorisation: - "/v1/persons/.*/needs" - "/v1/persons/.*/risks" - "/v1/persons/.*/reported-adjudications" + - "/v1/persons/.*/case-notes" - "/v1/epf/person-details/.*/.*" - "/health" - "/health/ping" diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 0307814e9..95974c846 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -17,6 +17,8 @@ services: base-url: http://localhost:4045 create-and-vary-licence: base-url: http://localhost:4070 + case-notes: + base-url: http://localhost:4080 hmpps.sqs: provider: localstack @@ -40,6 +42,7 @@ authorisation: - "/v1/persons/.*/needs" - "/v1/persons/.*/risks" - "/v1/persons/.*/reported-adjudications" + - "/v1/persons/.*/case-notes" - "/v1/epf/person-details/.*/.*" - "/health" - "/health/ping" diff --git a/src/main/resources/application-preprod.yml b/src/main/resources/application-preprod.yml index e2c88fb7a..6e741a2ea 100644 --- a/src/main/resources/application-preprod.yml +++ b/src/main/resources/application-preprod.yml @@ -15,6 +15,8 @@ services: base-url: https://manage-adjudications-api-preprod.hmpps.service.justice.gov.uk create-and-vary-licence: base-url: https://create-and-vary-a-licence-api-preprod.hmpps.service.justice.gov.uk + case-notes: + base-url: https://preprod.offender-case-notes.service.justice.gov.uk hmpps-auth: base-url: https://sign-in-preprod.hmpps.service.justice.gov.uk username: ${CLIENT_ID} diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index ccafdd5c0..ec6ca7dff 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -15,6 +15,8 @@ services: base-url: https://manage-adjudications-api-prod.hmpps.service.justice.gov.uk create-and-vary-licence: base-url: https://create-and-vary-a-licence-api-prod.hmpps.service.justice.gov.uk + case-notes: + base-url: https://prod.offender-case-notes.service.justice.gov.uk hmpps-auth: base-url: https://sign-in.hmpps.service.justice.gov.uk username: ${CLIENT_ID} diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml index 0166e7286..b9c50a71f 100644 --- a/src/main/resources/application-test.yml +++ b/src/main/resources/application-test.yml @@ -26,6 +26,8 @@ services: base-url: http://localhost:4006 create-and-vary-licence: base-url: http://localhost:4007 + case-notes: + base-url: http://localhost:4008 hmpps.sqs: provider: localstack @@ -52,9 +54,9 @@ authorisation: - "/v1/epf/person-details/.*/.*" - "/v1/persons/.*/adjudications" - "/v1/persons/.*/licences/conditions" + - "/v1/persons/.*/case-notes" - "/health" - "/health/ping" - "/health/readiness" - "/health/liveness" - "/info" - - diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f8c191dea..1765d3fb8 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -71,6 +71,8 @@ services: base-url: http://localhost:4045 create-and-vary-licence: base-url: http://localhost:4070 + case-notes: + base-url: http://localhost:4080 sentry: traces-sample-rate: "0.05" diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/controllers/v1/person/CaseNotesControllerTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/controllers/v1/person/CaseNotesControllerTest.kt new file mode 100755 index 000000000..b3c2a74e0 --- /dev/null +++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/controllers/v1/person/CaseNotesControllerTest.kt @@ -0,0 +1,91 @@ +package uk.gov.justice.digital.hmpps.hmppsintegrationapi.controllers.v1.person + +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import org.mockito.Mockito +import org.mockito.internal.verification.VerificationModeFactory +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest +import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.http.HttpStatus +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.web.servlet.MockMvc +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.extensions.removeWhitespaceAndNewlines +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.helpers.IntegrationAPIMockMvc +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.CaseNote +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Response +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.services.GetCaseNotesForPersonService +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.services.internal.AuditService +import java.net.URLEncoder +import java.nio.charset.StandardCharsets + +@WebMvcTest(controllers = [CaseNotesController::class]) +@ActiveProfiles("test") +class CaseNotesControllerTest( + @Autowired var springMockMvc: MockMvc, + @MockBean val getCaseNotesForPersonService: GetCaseNotesForPersonService, + @MockBean val auditService: AuditService, +) : DescribeSpec( + { + val hmppsId = "9999/11111A" + val encodedHmppsId = URLEncoder.encode(hmppsId, StandardCharsets.UTF_8) + val path = "/v1/persons/$encodedHmppsId/case-notes" + val mockMvc = IntegrationAPIMockMvc(springMockMvc) + val pageCaseNote = + + listOf( + CaseNote(caseNoteId = "abcd1234"), + + ) + describe("GET $path") { + beforeTest { + Mockito.reset(getCaseNotesForPersonService) + Mockito.reset(auditService) + whenever(getCaseNotesForPersonService.execute(hmppsId)).thenReturn( + Response( + data = pageCaseNote, + errors = emptyList(), + ), + ) + } + + it("returns a 200 OK status code") { + val result = mockMvc.performAuthorised(path) + + result.response.status.shouldBe(HttpStatus.OK.value()) + } + + it("gets the case notes for a person with the matching ID") { + mockMvc.performAuthorised(path) + + verify(getCaseNotesForPersonService, VerificationModeFactory.times(1)).execute(hmppsId) + } + + it("returns the case notes for a person with the matching ID") { + val result = mockMvc.performAuthorised(path) + + result.response.contentAsString.shouldContain( + """ + { + "data":[ + {"caseNoteId":"abcd1234"} + ], + "pagination":{ + "isLastPage":true,"count":1,"page":1,"perPage":10,"totalCount":1,"totalPages":1 + } + } + """.removeWhitespaceAndNewlines(), + ) + } + + it("logs audit") { + mockMvc.performAuthorised(path) + + verify(auditService, VerificationModeFactory.times(1)).createEvent("GET_CASE_NOTES", "Person case notes with hmpps id: $hmppsId has been retrieved") + } + } + }, +) diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/gateways/caseNotes/CaseNotesGatewayTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/gateways/caseNotes/CaseNotesGatewayTest.kt new file mode 100644 index 000000000..0341a1452 --- /dev/null +++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/gateways/caseNotes/CaseNotesGatewayTest.kt @@ -0,0 +1,56 @@ +package uk.gov.justice.digital.hmpps.hmppsintegrationapi.gateways.caseNotes + +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.matchers.shouldBe +import org.mockito.Mockito +import org.mockito.kotlin.whenever +import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer +import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.http.HttpStatus +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.context.ContextConfiguration +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.gateways.CaseNotesGateway +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.gateways.HmppsAuthGateway +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.mockservers.CaseNotesApiMockServer +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.mockservers.HmppsAuthMockServer +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.UpstreamApiError + +@ActiveProfiles("test") +@ContextConfiguration( + initializers = [ConfigDataApplicationContextInitializer::class], + classes = [CaseNotesGateway::class], +) +class CaseNotesGatewayTest( + @MockBean val hmppsAuthGateway: HmppsAuthGateway, + val caseNotesGateway: CaseNotesGateway, +) : DescribeSpec( + { + val caseNotesApiMockServer = CaseNotesApiMockServer() + beforeEach { + caseNotesApiMockServer.start() + + Mockito.reset(hmppsAuthGateway) + whenever(hmppsAuthGateway.getClientToken("CaseNotes")).thenReturn( + HmppsAuthMockServer.TOKEN, + ) + } + + afterTest { + caseNotesApiMockServer.stop() + } + + it("authenticates using HMPPS Auth with credentials") { + caseNotesGateway.getCaseNotesForPerson(id = "123") + + org.mockito.kotlin.verify(hmppsAuthGateway, org.mockito.internal.verification.VerificationModeFactory.times(1)) + .getClientToken("CaseNotes") + } + + it("upstream API returns an error, return error") { + caseNotesApiMockServer.stubGetCaseNotes("123", "", HttpStatus.BAD_REQUEST) + val response = caseNotesGateway.getCaseNotesForPerson(id = "123") + response.data.shouldBe(emptyList()) + response.errors[0].type.shouldBe(UpstreamApiError.Type.BAD_REQUEST) + } + }, +) diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/mockservers/CaseNotesApiMockServer.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/mockservers/CaseNotesApiMockServer.kt new file mode 100644 index 000000000..9d32a4e4f --- /dev/null +++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/mockservers/CaseNotesApiMockServer.kt @@ -0,0 +1,26 @@ +package uk.gov.justice.digital.hmpps.hmppsintegrationapi.mockservers + +import com.github.tomakehurst.wiremock.WireMockServer +import com.github.tomakehurst.wiremock.client.WireMock +import org.springframework.http.HttpStatus + +class CaseNotesApiMockServer : WireMockServer(WIREMOCK_PORT) { + companion object { + private const val WIREMOCK_PORT = 4008 + } + + fun stubGetCaseNotes(id: String, body: String, status: HttpStatus = HttpStatus.OK) { + stubFor( + WireMock.get("/case-notes/$id") + .withHeader( + "Authorization", + WireMock.matching("Bearer ${HmppsAuthMockServer.TOKEN}"), + ).willReturn( + WireMock.aResponse() + .withHeader("Content-Type", "application/json") + .withStatus(status.value()) + .withBody(body.trimIndent()), + ), + ) + } +} diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetCaseNotesForPersonServiceTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetCaseNotesForPersonServiceTest.kt new file mode 100755 index 000000000..c1398b248 --- /dev/null +++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetCaseNotesForPersonServiceTest.kt @@ -0,0 +1,58 @@ +package uk.gov.justice.digital.hmpps.hmppsintegrationapi.services + +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.matchers.shouldBe +import org.mockito.Mockito +import org.mockito.internal.verification.VerificationModeFactory +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer +import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.test.context.ContextConfiguration +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.gateways.CaseNotesGateway +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.CaseNote +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Identifiers +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Person +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Response + +@ContextConfiguration( + initializers = [ConfigDataApplicationContextInitializer::class], + classes = [GetCaseNotesForPersonService::class], +) +class GetCaseNotesForPersonServiceTest( + @MockBean val caseNotesGateway: CaseNotesGateway, + @MockBean val getPersonService: GetPersonService, + private val getCaseNoteForPersonService: GetCaseNotesForPersonService, +) : DescribeSpec( + { + val hmppsId = "1234/56789B" + val nomisNumber = "Z99999ZZ" + val person = Person(firstName = "Julianna", lastName = "Blake", identifiers = Identifiers(nomisNumber = nomisNumber)) + val caseNotes = + listOf( + CaseNote( + caseNoteId = "12345ABC", + ), + ) + + beforeEach { + Mockito.reset(getPersonService) + Mockito.reset(caseNotesGateway) + + whenever(getPersonService.execute(hmppsId = hmppsId)).thenReturn(Response(person)) + whenever(caseNotesGateway.getCaseNotesForPerson(id = nomisNumber)).thenReturn(Response(caseNotes)) + } + + it("performs a search according to hmpps Id") { + getCaseNoteForPersonService.execute(hmppsId) + verify(getPersonService, VerificationModeFactory.times(1)).execute(hmppsId = hmppsId) + } + + it("should return case notes from gateway") { + val result = getCaseNoteForPersonService.execute(hmppsId = hmppsId) + result.data.size.shouldBe(1) + result.data.first()?.caseNoteId.shouldBe("12345ABC") + result.errors.count().shouldBe(0) + } + }, +) diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/smoke/person/CaseNotesSmokeTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/smoke/person/CaseNotesSmokeTest.kt new file mode 100644 index 000000000..09675372d --- /dev/null +++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/smoke/person/CaseNotesSmokeTest.kt @@ -0,0 +1,43 @@ +package uk.gov.justice.digital.hmpps.hmppsintegrationapi.smoke.person + +import io.kotest.assertions.json.shouldEqualJson +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.matchers.shouldBe +import org.springframework.http.HttpStatus +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.extensions.removeWhitespaceAndNewlines +import uk.gov.justice.digital.hmpps.hmppsintegrationapi.helpers.IntegrationAPIHttpClient +import java.net.URLEncoder +import java.nio.charset.StandardCharsets + +class CaseNotesSmokeTest : DescribeSpec( + { + val hmppsId = "ABC123" + val encodedHmppsId = URLEncoder.encode(hmppsId, StandardCharsets.UTF_8) + val basePath = "v1/persons/$encodedHmppsId/case-notes" + val httpClient = IntegrationAPIHttpClient() + + it("returns case notes for a person.") { + val response = httpClient.performAuthorised(basePath) + response.statusCode().shouldBe(HttpStatus.OK.value()) + response.body().shouldEqualJson( + """ + { + "data": [ + { + "caseNoteId": "12311312" + } + ], + "pagination": { + "isLastPage": true, + "count": 1, + "page": 1, + "perPage": 10, + "totalCount": 1, + "totalPages": 1 + } + } + """.removeWhitespaceAndNewlines(), + ) + } + }, +)