Skip to content

Commit 8cbf357

Browse files
committed
PI-2469: Added reference data endpoint
1 parent b93c9e4 commit 8cbf357

File tree

15 files changed

+662
-1
lines changed

15 files changed

+662
-1
lines changed

build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ dependencies {
2727
testImplementation("org.wiremock:wiremock-standalone:3.2.0")
2828
testImplementation("io.kotest.extensions:kotest-extensions-spring:1.1.3")
2929
testImplementation("org.mockito:mockito-core:5.7.0")
30+
testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.2")
3031

3132
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
3233
}

gradle.properties

-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,3 @@
66
#
77
# To stop the dps-gradle-spring-boot project from overwriting any project specific customisations here, remove the
88
# warning at the top of this file.
9-
kotlin.incremental.useClasspathSnapshot=false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
package uk.gov.justice.digital.hmpps.hmppsintegrationapi.controllers.v1
2+
3+
import ReferenceData
4+
import io.swagger.v3.oas.annotations.Hidden
5+
import io.swagger.v3.oas.annotations.Operation
6+
import io.swagger.v3.oas.annotations.media.Content
7+
import io.swagger.v3.oas.annotations.media.ExampleObject
8+
import io.swagger.v3.oas.annotations.media.Schema
9+
import io.swagger.v3.oas.annotations.responses.ApiResponse
10+
import org.springframework.boot.context.properties.EnableConfigurationProperties
11+
import org.springframework.web.bind.annotation.GetMapping
12+
import org.springframework.web.bind.annotation.RequestMapping
13+
import org.springframework.web.bind.annotation.RestController
14+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.config.AuthorisationConfig
15+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Response
16+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.services.ReferenceDataService
17+
18+
@Hidden
19+
@RestController
20+
@EnableConfigurationProperties(AuthorisationConfig::class)
21+
@RequestMapping("/v1/reference-data")
22+
class ReferenceDataController(
23+
var referenceDataService: ReferenceDataService,
24+
) {
25+
@GetMapping
26+
@Operation(
27+
summary = """
28+
Returns probation and prison reference data.
29+
> Prison Reference Data Types: PHONE_TYPE, ALERT_TYPE, ETHNICITY, GENDER
30+
> Probation Reference Data Types: PHONE_TYPE, REGISTER_TYPE, ETHNICITY, GENDER
31+
""",
32+
responses = [
33+
ApiResponse(
34+
responseCode = "200",
35+
description = "Successfully returned prison and probation reference data.",
36+
content = [
37+
Content(
38+
schema = Schema(implementation = Response::class),
39+
examples = [
40+
ExampleObject(
41+
"""{
42+
"data": {
43+
"prisonReferenceData": {
44+
"PHONE_TYPE": [
45+
{
46+
"code": "a",
47+
"description": "desc_a"
48+
},
49+
{
50+
"code": "b",
51+
"description": "desc_b"
52+
},
53+
{
54+
"code": "c",
55+
"description": "desc_c"
56+
}
57+
],
58+
"ALERT_TYPE": [
59+
{
60+
"code": "a",
61+
"description": "desc_a"
62+
},
63+
{
64+
"code": "b",
65+
"description": "desc_b"
66+
},
67+
{
68+
"code": "c",
69+
"description": "desc_c"
70+
}
71+
],
72+
"ETHNICITY": [
73+
{
74+
"code": "a",
75+
"description": "desc_a"
76+
},
77+
{
78+
"code": "b",
79+
"description": "desc_b"
80+
},
81+
{
82+
"code": "c",
83+
"description": "desc_c"
84+
}
85+
],
86+
"GENDER": [
87+
{
88+
"code": "a",
89+
"description": "desc_a"
90+
},
91+
{
92+
"code": "b",
93+
"description": "desc_b"
94+
},
95+
{
96+
"code": "c",
97+
"description": "desc_c"
98+
}
99+
]
100+
},
101+
"probationReferenceData": {
102+
"PHONE_TYPE": [
103+
{
104+
"code": "a",
105+
"description": "desc_a"
106+
},
107+
{
108+
"code": "b",
109+
"description": "desc_b"
110+
},
111+
{
112+
"code": "c",
113+
"description": "desc_c"
114+
}
115+
],
116+
"REGISTER_TYPE": [
117+
{
118+
"code": "a",
119+
"description": "desc_a"
120+
},
121+
{
122+
"code": "b",
123+
"description": "desc_b"
124+
},
125+
{
126+
"code": "c",
127+
"description": "desc_c"
128+
}
129+
],
130+
"ETHNICITY": [
131+
{
132+
"code": "a",
133+
"description": "desc_a"
134+
},
135+
{
136+
"code": "b",
137+
"description": "desc_b"
138+
},
139+
{
140+
"code": "c",
141+
"description": "desc_c"
142+
}
143+
],
144+
"GENDER": [
145+
{
146+
"code": "a",
147+
"description": "desc_a"
148+
},
149+
{
150+
"code": "b",
151+
"description": "desc_b"
152+
},
153+
{
154+
"code": "c",
155+
"description": "desc_c"
156+
}
157+
]
158+
},
159+
"errors": []
160+
}""",
161+
),
162+
],
163+
),
164+
],
165+
),
166+
ApiResponse(responseCode = "500", content = [Content(schema = Schema(ref = "#/components/schemas/InternalServerError"))]),
167+
],
168+
)
169+
fun getReferenceData(): Response<ReferenceData?> {
170+
return referenceDataService.referenceData()
171+
}
172+
}

src/main/kotlin/uk/gov/justice/digital/hmpps/hmppsintegrationapi/extensions/WebClientWrapper.kt

+9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import org.springframework.web.reactive.function.BodyInserters
66
import org.springframework.web.reactive.function.client.ExchangeStrategies
77
import org.springframework.web.reactive.function.client.WebClient
88
import org.springframework.web.reactive.function.client.WebClientResponseException
9+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Response
910
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.UpstreamApi
1011
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.UpstreamApiError
1112

@@ -112,3 +113,11 @@ class WebClientWrapper(
112113
)
113114
}
114115
}
116+
117+
inline fun <reified T> WebClientWrapper.WebClientWrapperResponse<T>.getOrError(error: (errors: List<UpstreamApiError>) -> Response<Any?>): T {
118+
if (this is WebClientWrapper.WebClientWrapperResponse.Error) {
119+
error(this.errors)
120+
}
121+
val success = this as WebClientWrapper.WebClientWrapperResponse.Success
122+
return success.data
123+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
data class ReferenceData(
2+
val prisonReferenceData: Map<String, List<ReferenceDataItem>>?,
3+
val probationReferenceData: Map<String, List<ReferenceDataItem>>?,
4+
)
5+
6+
data class ReferenceDataItem(
7+
val code: String,
8+
val description: String,
9+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package uk.gov.justice.digital.hmpps.hmppsintegrationapi.services
2+
3+
import ReferenceData
4+
import ReferenceDataItem
5+
import org.springframework.beans.factory.annotation.Value
6+
import org.springframework.http.HttpMethod
7+
import org.springframework.stereotype.Service
8+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.extensions.WebClientWrapper
9+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.extensions.getOrError
10+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.gateways.HmppsAuthGateway
11+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Response
12+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.UpstreamApi
13+
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.nomis.NomisReferenceCode
14+
15+
@Service
16+
class ReferenceDataService(
17+
@Value("\${services.ndelius.base-url}") deliusBaseUrl: String,
18+
@Value("\${services.prison-api.base-url}") prisonBaseUrl: String,
19+
private val hmppsAuthGateway: HmppsAuthGateway,
20+
) {
21+
private val deliusWebClient = WebClientWrapper(deliusBaseUrl)
22+
private val prisonApiWebclient = WebClientWrapper(prisonBaseUrl)
23+
24+
fun referenceData(): Response<ReferenceData?> {
25+
val probationReferenceData =
26+
deliusWebClient.request<ReferenceData>(
27+
HttpMethod.GET,
28+
"/reference-data",
29+
authHeader(),
30+
UpstreamApi.NDELIUS,
31+
).getOrError { (errors) -> return Response(null, errors = listOf(errors)) }.probationReferenceData
32+
33+
val prisonReferenceData =
34+
NomisReferenceDataType.entries.flatMap {
35+
val rd = prisonReferenceData(it.name)
36+
if (rd.errors.isNotEmpty()) {
37+
return Response(data = null, errors = rd.errors)
38+
}
39+
rd.data!!
40+
}.groupByTo(LinkedHashMap(), { NomisReferenceDataType.valueOf(it.domain!!).category }, { ReferenceDataItem(it.code!!, it.description!!) })
41+
42+
return Response(data = ReferenceData(prisonReferenceData, probationReferenceData))
43+
}
44+
45+
private fun prisonReferenceData(domain: String): Response<List<NomisReferenceCode>?> {
46+
val prisonReferenceData =
47+
prisonApiWebclient.requestList<NomisReferenceCode>(
48+
HttpMethod.GET,
49+
"/api/reference-domains/domains/$domain",
50+
prisonAuthHeader(),
51+
UpstreamApi.NOMIS,
52+
).getOrError { (errors) -> return Response(null, errors = listOf(errors)) }
53+
return Response(data = prisonReferenceData)
54+
}
55+
56+
private fun authHeader(): Map<String, String> {
57+
val token = hmppsAuthGateway.getClientToken("nDelius")
58+
return mapOf(
59+
"Authorization" to "Bearer $token",
60+
)
61+
}
62+
63+
private fun prisonAuthHeader(): Map<String, String> {
64+
val token = hmppsAuthGateway.getClientToken("NOMIS")
65+
val version = "1.0"
66+
67+
return mapOf(
68+
"Authorization" to "Bearer $token",
69+
"version" to version,
70+
"Page-Limit" to Int.MAX_VALUE.toString(),
71+
)
72+
}
73+
}
74+
75+
enum class NomisReferenceDataType(val category: String) {
76+
PHONE_USAGE("PHONE_TYPE"),
77+
ALERT("ALERT_TYPE"),
78+
ETHNICITY("ETHNICITY"),
79+
SEX("GENDER"),
80+
}

src/main/resources/application-dev.yml

+3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ authorisation:
4848
- "/v1/persons/.*/risks/categories"
4949
- "/v1/persons/.*/person-responsible-officer"
5050
- "/v1/persons/.*/risk-management-plan"
51+
- "/v1/reference-data"
5152
ctrlo:
5253
- "/v1/epf/person-details/.*/[^/]*$"
5354
moj-pes:
@@ -72,6 +73,7 @@ authorisation:
7273
- "/v1/persons/.*/licences/conditions"
7374
- "/v1/persons/.*/person-responsible-officer"
7475
- "/v1/persons/.*/status-information"
76+
- "/v1/reference-data"
7577
event-service:
7678
- "/v1/config/authorisation"
7779
mryall:
@@ -87,3 +89,4 @@ authorisation:
8789
- "/v1/persons/.*/licences/conditions"
8890
- "/v1/persons/.*/person-responsible-officer"
8991
- "/v1/persons/.*/status-information"
92+
- "/v1/reference-data"

src/main/resources/application-local.yml

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ authorisation:
5555
- "/health/readiness"
5656
- "/health/liveness"
5757
- "/info"
58+
- "/v1/reference-data"
5859
config-test:
5960
- "/v1/config/authorisation"
6061
all-access:

src/main/resources/application-preprod.yml

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ authorisation:
3232
- "/v1/persons/.*/name"
3333
- "/v1/hmpps-id/nomis-number/[^/]*$"
3434
- "/v1/persons/.*/cell-location"
35+
- "/v1/reference-data"
3536
kubernetes-health-check-client:
3637
- "/health/liveness"
3738
- "/health/readiness"

src/main/resources/application-prod.yml

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ authorisation:
3232
- "/v1/persons/.*/name"
3333
- "/v1/hmpps-id/nomis-number/[^/]*$"
3434
- "/v1/persons/.*/cell-location"
35+
- "/v1/reference-data"
3536
kubernetes-health-check-client:
3637
- "/health/liveness"
3738
- "/health/readiness"

src/main/resources/application-test.yml

+1
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,6 @@ authorisation:
7373
- "/health/readiness"
7474
- "/health/liveness"
7575
- "/info"
76+
- "/v1/reference-data"
7677
config-test:
7778
- "/v1/config/authorisation"

0 commit comments

Comments
 (0)