Skip to content

Commit 07843dc

Browse files
Merge branch 'main' into HIA-721
2 parents c4a1116 + 93bf6ff commit 07843dc

File tree

8 files changed

+237
-97
lines changed

8 files changed

+237
-97
lines changed

docs/guides/setting-up-a-new-consumer.md

+16-2
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,17 @@ per environment.
1212
- [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl)
1313
- [Access to Cloud Platform’s Kubernetes cluster](https://user-guide.cloud-platform.service.justice.gov.uk/documentation/getting-started/kubectl-config.html#installing-kubectl)
1414

15+
As a pre-requisite to create a client certificate by running the script below, please ensure that:
16+
- You are logged in so that you can access AWS resources via the CLI.
17+
- Verify that you have all your AWS credentials ready by opening the config file in the ".aws" directory.
18+
1519
## Create a client certificate
1620

17-
1. Run the [generate-client-certificate.sh](/scripts/client_certificates/generate.sh) script with the name of the environment and client.
21+
Run the [generate-client-certificate.sh](/scripts/client_certificates/generate.sh) script with the name of the environment and client.
1822

1923
```bash
2024
make generate-client-certificate
2125
```
22-
2326
This will output three files in the ./scripts/client_certificates directory:
2427

2528
- a private key e.g. `dev-nhs-client.key`
@@ -114,3 +117,14 @@ kubectl -n hmpps-integration-api-[environment] get secrets [your queue secret na
114117
5. Create new [Cloud Platform Environments GitHub repository](https://github.com/ministryofjustice/cloud-platform-environments/tree/main) branch
115118
6. Update terraform to load the secret value from AWS and update filter_policy value. Follow [Example](https://github.com/ministryofjustice/cloud-platform-environments/pull/22111/files). Note: The name of aws_secretsmanager_secret module has to be same as the secret name created from step 4/5 above.
116119
7. Follow steps 3-8 in [Create an API key](#create-an-api-key) to merge branch to main.
120+
121+
## Create a new endpoint for a client
122+
123+
### Create basic infrastructure
124+
Within the [Cloud Platform Environments GitHub repository](https://github.com/ministryofjustice/cloud-platform-environments/tree/main) and the namespace of the environment:
125+
126+
1. Create a branch.
127+
2. Add a new API Gateway resource, a SQS method, a SQS method response, and an integration. Example: [api_gateway.tf](https://github.com/ministryofjustice/cloud-platform-environments/pull/22695/files)
128+
3. Ensure that all the permissions are up-to-date and add a new role and policy for your new resource. Example: [iam.tf](https://github.com/ministryofjustice/cloud-platform-environments/pull/22787/files#diff-a376622fa4a4c2fd9404d5ee4221487259264608a0cbe36b99c150c472558f29)
129+
4. Check that the integration is pointing to the right queue. Example: [api_gateway.tf](https://github.com/ministryofjustice/cloud-platform-environments/pull/22795/files)
130+
5. Deploy and test (do not use Postman, rather use a GET cURL command with "x-api-key" as your header.)

openapi.yml

+15-12
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ paths:
9696
type: object
9797
properties:
9898
data:
99+
type: object
100+
properties:
101+
prisonerOffenderSearch:
102+
$ref: "#/components/schemas/Person"
103+
probationOffenderSearch:
99104
$ref: "#/components/schemas/Person"
100105
"404":
101106
description: Failed to find a person with the provided HMPPS ID.
@@ -1722,45 +1727,44 @@ components:
17221727
firstName:
17231728
type: string
17241729
example: Arthur
1725-
description: first name
1730+
description: First name
17261731
middleName:
17271732
type: string
17281733
example: John
17291734
nullable: true
1730-
description: middle name
1735+
description: Middle name
17311736
lastName:
17321737
type: string
17331738
example: Morgan
1734-
description: last name
1739+
description: Last name
17351740
dateOfBirth:
17361741
type: string
17371742
format: date
17381743
example: 1965-12-01
1739-
description: date of birth
1744+
description: Date of birth
17401745
gender:
17411746
type: string
17421747
example: Male
1743-
description: gender
1748+
description: Gender
17441749
ethnicity:
17451750
type: string
17461751
example: "White: Eng./Welsh/Scot./N.Irish/British"
1747-
description: ethnicity
1752+
nullable: true
1753+
description: Ethnicity
17481754
aliases:
17491755
type: array
1750-
minItems: 0
17511756
items:
17521757
$ref: "#/components/schemas/Alias"
1753-
description: list of aliases
1758+
description: List of aliases
17541759
identifiers:
17551760
$ref: "#/components/schemas/Identifiers"
17561761
pncId:
17571762
type: string
1758-
example: 2008/0545166T
17591763
description: An identifier from the Police National Computer (PNC)
17601764
hmppsId:
17611765
type: string
1762-
example: X00001
1763-
description: Currently a hmppsId is a CRN identifier however this will change in the future to be a new unique Hmpps identifier
1766+
example: 2008/0545166T
1767+
description: Hmpps identifier
17641768
contactDetails:
17651769
$ref: "#/components/schemas/ContactDetails"
17661770
PersonResponsibleOfficerName:
@@ -1902,7 +1906,6 @@ components:
19021906
nullable: true
19031907
prison:
19041908
$ref: "#/components/schemas/Prison"
1905-
19061909
Punishment:
19071910
type: object
19081911
properties:

scripts/client_certificates/generate.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ upload_backup() {
5151
aws s3api put-object --bucket "$bucket" --key "$client_folder/" --acl private
5252
aws s3 cp "$file_path" "s3://$path/client.pem"
5353
aws s3 cp ./"$environment"-"$client"-client.pem "s3://$path/client.pem"
54-
aws s3 cp ./truststore.key "s3://$path/truststore.key"
55-
aws s3 cp ./truststore.pem "s3://$path/truststore.pem"
54+
aws s3 cp ./"$environment"-"$client"-client.csr "s3://$path/client.csr"
55+
aws s3 cp ./"$environment"-"$client"-client.key "s3://$path/client.key"
5656

5757
aws configure set aws_access_key_id ""
5858
aws configure set aws_secret_access_key ""

src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/controllers/v1/person/PersonController.kt

+4-3
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,17 @@ class PersonController(
6161
@GetMapping("{encodedHmppsId}")
6262
fun getPerson(
6363
@PathVariable encodedHmppsId: String,
64-
): Map<String, Person?> {
64+
): Map<String, Map<String, Person?>> {
6565
val hmppsId = encodedHmppsId.decodeUrlCharacters()
66-
val response = getPersonService.execute(hmppsId)
66+
val response = getPersonService.getCombinedDataForPerson(hmppsId)
6767

6868
if (response.hasErrorCausedBy(ENTITY_NOT_FOUND, causedBy = UpstreamApi.PROBATION_OFFENDER_SEARCH)) {
6969
throw EntityNotFoundException("Could not find person with id: $hmppsId")
7070
}
7171

7272
auditService.createEvent("GET_PERSON_DETAILS", mapOf("hmppsId" to hmppsId))
73-
return mapOf("data" to response.data)
73+
val data = response.data.mapValues { it.value }
74+
return mapOf("data" to data)
7475
}
7576

7677
@GetMapping("{encodedHmppsId}/images")

src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetPersonService.kt

+31
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,48 @@ package uk.gov.justice.digital.hmpps.hmppsintegrationapi.services
22

33
import org.springframework.beans.factory.annotation.Autowired
44
import org.springframework.stereotype.Service
5+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.gateways.PrisonerOffenderSearchGateway
56
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.gateways.ProbationOffenderSearchGateway
67
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Person
78
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Response
89

910
@Service
1011
class GetPersonService(
1112
@Autowired val probationOffenderSearchGateway: ProbationOffenderSearchGateway,
13+
@Autowired val prisonerOffenderSearchGateway: PrisonerOffenderSearchGateway,
1214
) {
1315
fun execute(hmppsId: String): Response<Person?> {
1416
val personFromProbationOffenderSearch = probationOffenderSearchGateway.getPerson(id = hmppsId)
1517

1618
return Response(data = personFromProbationOffenderSearch.data, errors = personFromProbationOffenderSearch.errors)
1719
}
20+
21+
fun getCombinedDataForPerson(hmppsId: String): Response<Map<String, Person?>> {
22+
val probationResponse = probationOffenderSearchGateway.getPerson(id = hmppsId)
23+
24+
val prisonResponse =
25+
probationResponse.data?.identifiers?.nomisNumber?.let {
26+
prisonerOffenderSearchGateway.getPrisonOffender(nomsNumber = it)
27+
}
28+
29+
return if (prisonResponse != null) {
30+
Response(
31+
data =
32+
mapOf(
33+
"prisonerOffenderSearch" to prisonResponse.data?.toPerson(),
34+
"probationOffenderSearch" to probationResponse.data,
35+
),
36+
errors = prisonResponse.errors + probationResponse.errors,
37+
)
38+
} else {
39+
Response(
40+
data =
41+
mapOf(
42+
"prisonerOffenderSearch" to null,
43+
"probationOffenderSearch" to probationResponse.data,
44+
),
45+
errors = probationResponse.errors,
46+
)
47+
}
48+
}
1849
}

src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/controllers/v1/person/PersonControllerTest.kt

+67-26
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ import org.springframework.web.reactive.function.client.WebClientResponseExcepti
2020
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.extensions.removeWhitespaceAndNewlines
2121
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.helpers.IntegrationAPIMockMvc
2222
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.ContactDetailsWithEmailAndPhone
23+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Identifiers
2324
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.ImageMetadata
2425
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Person
2526
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.PhoneNumber
2627
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Response
2728
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.UpstreamApi
2829
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.UpstreamApiError
30+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.prisoneroffendersearch.POSPrisoner
2931
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.services.GetImageMetadataForPersonService
3032
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.services.GetPersonService
3133
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.services.GetPersonsService
@@ -222,12 +224,20 @@ internal class PersonControllerTest(
222224
}
223225

224226
describe("GET $basePath/{id}") {
225-
val person = Person("Silly", "Sobbers")
227+
val probationOffenderSearch = Person("Sam", "Smith", identifiers = Identifiers(nomisNumber = "1234ABC"))
228+
val prisonOffenderSearch = POSPrisoner("Kim", "Kardashian")
229+
val prisonResponse = Response(data = prisonOffenderSearch, errors = emptyList())
226230

227231
beforeTest {
228232
Mockito.reset(getPersonService)
229233
Mockito.reset(auditService)
230-
whenever(getPersonService.execute(hmppsId)).thenReturn(Response(data = person))
234+
235+
val personMap =
236+
mapOf(
237+
"probationOffenderSearch" to probationOffenderSearch,
238+
"prisonerOffenderSearch" to prisonResponse.data.toPerson(),
239+
)
240+
whenever(getPersonService.getCombinedDataForPerson(hmppsId)).thenReturn(Response(data = personMap))
231241
}
232242

233243
it("returns a 200 OK status code") {
@@ -248,9 +258,13 @@ internal class PersonControllerTest(
248258
val idThatDoesNotExist = "9999/11111Z"
249259

250260
it("returns a 404 status code when a person cannot be found in both upstream APIs") {
251-
whenever(getPersonService.execute(idThatDoesNotExist)).thenReturn(
261+
whenever(getPersonService.getCombinedDataForPerson(idThatDoesNotExist)).thenReturn(
252262
Response(
253-
data = null,
263+
data =
264+
mapOf(
265+
"prisonerOffenderSearch" to null,
266+
"probationOffenderSearch" to null,
267+
),
254268
errors =
255269
listOf(
256270
UpstreamApiError(
@@ -268,9 +282,13 @@ internal class PersonControllerTest(
268282
}
269283

270284
it("does not return a 404 status code when a person was found in one upstream API") {
271-
whenever(getPersonService.execute(idThatDoesNotExist)).thenReturn(
285+
whenever(getPersonService.getCombinedDataForPerson(idThatDoesNotExist)).thenReturn(
272286
Response(
273-
data = Person("someFirstName", "someLastName"),
287+
data =
288+
mapOf(
289+
"prisonerOffenderSearch" to null,
290+
"probationOffenderSearch" to Person("someFirstName", "someLastName"),
291+
),
274292
errors =
275293
listOf(
276294
UpstreamApiError(
@@ -291,33 +309,56 @@ internal class PersonControllerTest(
291309
it("gets a person with the matching ID") {
292310
mockMvc.performAuthorised("$basePath/$encodedHmppsId")
293311

294-
verify(getPersonService, times(1)).execute(hmppsId)
312+
verify(getPersonService, times(1)).getCombinedDataForPerson(hmppsId)
295313
}
296314

297315
it("returns a person with the matching ID") {
298316
val result = mockMvc.performAuthorised("$basePath/$encodedHmppsId")
299317

300318
result.response.contentAsString.shouldBe(
301319
"""
302-
{
303-
"data": {
304-
"firstName": "Silly",
305-
"lastName": "Sobbers",
306-
"middleName": null,
307-
"dateOfBirth": null,
308-
"gender": null,
309-
"ethnicity": null,
310-
"aliases": [],
311-
"identifiers": {
312-
"nomisNumber": null,
313-
"croNumber": null,
314-
"deliusCrn": null
315-
},
316-
"pncId": null,
317-
"hmppsId": null,
318-
"contactDetails": null
319-
}
320-
}
320+
{
321+
"data":{
322+
"probationOffenderSearch":{
323+
"firstName":"Sam",
324+
"lastName":"Smith",
325+
"middleName":null,
326+
"dateOfBirth":null,
327+
"gender":null,
328+
"ethnicity":null,
329+
"aliases":[
330+
331+
],
332+
"identifiers":{
333+
"nomisNumber":"1234ABC",
334+
"croNumber":null,
335+
"deliusCrn":null
336+
},
337+
"pncId":null,
338+
"hmppsId":null,
339+
"contactDetails":null
340+
},
341+
"prisonerOffenderSearch":{
342+
"firstName":"Kim",
343+
"lastName":"Kardashian",
344+
"middleName":null,
345+
"dateOfBirth":null,
346+
"gender":null,
347+
"ethnicity":null,
348+
"aliases":[
349+
350+
],
351+
"identifiers":{
352+
"nomisNumber":null,
353+
"croNumber":null,
354+
"deliusCrn":null
355+
},
356+
"pncId":null,
357+
"hmppsId":null,
358+
"contactDetails":null
359+
}
360+
}
361+
}
321362
""".removeWhitespaceAndNewlines(),
322363
)
323364
}

src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/services/GetPersonServiceTest.kt

+22
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import uk.gov.justice.digital.hmpps.hmppsintegrationapi.gateways.ProbationOffend
1414
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Identifiers
1515
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Person
1616
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Response
17+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.prisoneroffendersearch.POSPrisoner
1718

1819
@ContextConfiguration(
1920
initializers = [ConfigDataApplicationContextInitializer::class],
@@ -36,6 +37,9 @@ internal class GetPersonServiceTest(
3637
whenever(probationOffenderSearchGateway.getPerson(id = hmppsId)).thenReturn(
3738
Response(data = Person(firstName = "Qui-gon", lastName = "Jin", identifiers = Identifiers(nomisNumber = "A1234AA"))),
3839
)
40+
whenever(prisonerOffenderSearchGateway.getPrisonOffender(nomsNumber = "A1234AA")).thenReturn(
41+
Response(data = POSPrisoner(firstName = "Sam", lastName = "Mills")),
42+
)
3943
}
4044

4145
it("gets a person from Probation Offender Search") {
@@ -66,4 +70,22 @@ internal class GetPersonServiceTest(
6670

6771
result.data.shouldBe(expectedResult)
6872
}
73+
74+
it("returns a person with both probation and prison data when prison data exists") {
75+
val personFromProbationOffenderSearch = Person("Paula", "First", identifiers = Identifiers(nomisNumber = "A1234AA"))
76+
val personFromPrisonOffenderSearch = POSPrisoner("Sam", "Mills")
77+
78+
whenever(probationOffenderSearchGateway.getPerson(hmppsId)).thenReturn(
79+
Response(data = personFromProbationOffenderSearch),
80+
)
81+
whenever(prisonerOffenderSearchGateway.getPrisonOffender("A1234AA")).thenReturn(
82+
Response(data = personFromPrisonOffenderSearch),
83+
)
84+
85+
val result = getPersonService.getCombinedDataForPerson(hmppsId)
86+
val expectedResult = result.data
87+
88+
result.data.shouldBe(expectedResult)
89+
result.errors shouldBe emptyList()
90+
}
6991
})

0 commit comments

Comments
 (0)