Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement CVL golden record #357

Merged
merged 2 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@ 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.Licence
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.PersonLicences
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.UpstreamApiError
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.services.GetLicenceConditionService
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")
Expand All @@ -25,16 +22,14 @@ class LicenceConditionController(
@GetMapping("{encodedHmppsId}/licences/conditions")
fun getLicenceConditions(
@PathVariable encodedHmppsId: String,
@RequestParam(required = false, defaultValue = "1", name = "page") page: Int,
@RequestParam(required = false, defaultValue = "8", name = "perPage") perPage: Int,
): PaginatedResponse<Licence> {
): Map<String, PersonLicences?> {
val hmppsId = encodedHmppsId.decodeUrlCharacters()
val response = getLicenceConditionService.execute(hmppsId)

if (response.hasError(UpstreamApiError.Type.ENTITY_NOT_FOUND)) {
throw EntityNotFoundException("Could not find person with id: $hmppsId")
}
auditService.createEvent("GET_PERSON_LICENCE_CONDITION", "Person licence condition details with hmpps id: $hmppsId has been retrieved")
return response.data.paginateWith(page, perPage)
return mapOf("data" to response.data)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ package uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.createAndVaryLic

class CvlAPCondition(
val standard: List<CvlCondition>? = null,
val bespoke: List<CvlCondition>? = null,
val additional: List<CvlCondition>? = null,

)
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ package uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.createAndVaryLic
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.LicenceCondition

class CvlCondition(
var type: String? = null,
var code: String? = null,
var category: String? = null,
val text: String? = null,
) {
fun toLicenceCondition(): LicenceCondition = LicenceCondition(
fun toLicenceCondition(parentType: String? = null): LicenceCondition = LicenceCondition(
condition = this.text,
category = this.category,
code = this.code,
type = parentType ?: this.type,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,5 @@ import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.LicenceCond
class CvlLicence(
val conditions: CvlLicenceCondition? = null,
) {
fun toLicenceConditions(): List<LicenceCondition> {
val result = mutableListOf<LicenceCondition>()
conditions?.AP?.standard?.forEach { result.add(it.toLicenceCondition()) }
return result
}
fun toLicenceConditions(): List<LicenceCondition> = conditions?.toLicenceConditions() ?: emptyList()
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
package uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.createAndVaryLicence

import com.fasterxml.jackson.annotation.JsonProperty
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.LicenceCondition

class CvlLicenceCondition(
@JsonProperty("AP")
val AP: CvlAPCondition? = null,
)
@JsonProperty("PSS")
val PSS: CvlPSSCondition? = null,
) {
fun toLicenceConditions(): List<LicenceCondition> {
val conditions = mutableListOf<LicenceCondition>()

AP?.bespoke?.let { it -> conditions.addAll(it.map { it.toLicenceCondition("Bespoke") }) }
AP?.standard?.let { it -> conditions.addAll(it.map { it.toLicenceCondition("Standard") }) }
AP?.additional?.let { it -> conditions.addAll(it.map { it.toLicenceCondition() }) }
PSS?.additional?.let { it -> conditions.addAll(it.map { it.toLicenceCondition() }) }
PSS?.standard?.let { it -> conditions.addAll(it.map { it.toLicenceCondition("Standard") }) }
return conditions
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,19 @@ import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Licence
data class CvlLicenceSummary(
val id: String,
val prisonNumber: String? = null,
val statusCode: String? = null,
val licenceType: String? = null,
val createdDateTime: String? = null,
val approvedDateTime: String? = null,
val updatedDateTime: String? = null,
) {
fun toLicence(): Licence = Licence(
id = this.id,
offenderNumber = this.prisonNumber,
status = this.statusCode,
typeCode = this.licenceType,
createdDate = this.createdDateTime,
approvedDate = this.approvedDateTime,
updatedDate = this.updatedDateTime,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.createAndVaryLicence

class CvlPSSCondition(
val standard: List<CvlCondition>? = null,
val additional: List<CvlCondition>? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import com.fasterxml.jackson.annotation.JsonIgnore
data class Licence(
@JsonIgnore
val id: String,
@JsonIgnore
val offenderNumber: String? = null,
val status: String? = null,
val typeCode: String? = null,
val createdDate: String? = null,
val approvedDate: String? = null,
val updatedDate: String? = null,
var conditions: List<LicenceCondition> = emptyList(),
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps

class LicenceCondition(
val type: String? = null,
val code: String? = null,
val category: String? = null,
val condition: String? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps

data class PersonLicences(
val hmppsId: String,
val offenderNumber: String? = null,
val licences: List<Licence> = emptyList(),
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,31 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.gateways.CreateAndVaryLicenceGateway
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Licence
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.PersonLicences
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Response

@Service
class GetLicenceConditionService(
@Autowired val createAndVaryLicenceGateway: CreateAndVaryLicenceGateway,
@Autowired val getPersonService: GetPersonService,
) {
fun execute(hmppsId: String): Response<List<Licence>> {
fun execute(hmppsId: String): Response<PersonLicences> {
val personResponse = getPersonService.execute(hmppsId = hmppsId)
val crn = personResponse.data?.identifiers?.deliusCrn

var licences: Response<List<Licence>> = Response(data = emptyList())

var personLicences = PersonLicences(hmppsId)
if (crn != null) {
licences = createAndVaryLicenceGateway.getLicenceSummaries(id = crn)
licences.data.forEach {
val conditions = createAndVaryLicenceGateway.getLicenceConditions(it.id)
it.conditions = conditions.data
}
personLicences = PersonLicences(hmppsId, licences.data.firstOrNull()?.offenderNumber, licences.data)
}

return Response(
data = licences.data,
data = personLicences,
errors = personResponse.errors + licences.errors,
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package uk.gov.justice.digital.hmpps.hmppsintegrationapi.controllers.v1.person

import io.kotest.assertions.json.shouldContainJsonKeyValue
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
Expand All @@ -13,9 +13,11 @@ 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.Licence
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.LicenceCondition
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.PersonLicences
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.hmpps.UpstreamApiError
Expand Down Expand Up @@ -43,10 +45,13 @@ class LicenceConditionControllerTests(
Mockito.reset(auditService)
whenever(getLicenceConditionService.execute(hmppsId)).thenReturn(
Response(
data = listOf(
Licence(
id = "MockId",
conditions = listOf(LicenceCondition(condition = "MockCondition")),
data = PersonLicences(
hmppsId = hmppsId,
licences = listOf(
Licence(
id = "MockId",
conditions = listOf(LicenceCondition(condition = "MockCondition")),
),
),
),
),
Expand All @@ -55,8 +60,8 @@ class LicenceConditionControllerTests(

it("throws exception when no person found") {
whenever(getLicenceConditionService.execute(hmppsId = "notfound")).thenReturn(
Response(
data = emptyList(),
Response<PersonLicences>(
data = PersonLicences(hmppsId = hmppsId, licences = emptyList()),
errors = listOf(
UpstreamApiError(
type = UpstreamApiError.Type.ENTITY_NOT_FOUND,
Expand All @@ -76,21 +81,33 @@ class LicenceConditionControllerTests(
verify(auditService, VerificationModeFactory.times(1)).createEvent("GET_PERSON_LICENCE_CONDITION", "Person licence condition details with hmpps id: $hmppsId has been retrieved")
}

it("returns paginated licence condition results") {
whenever(getLicenceConditionService.execute(hmppsId)).thenReturn(
Response(
data = List(20) {
Licence(
id = "MockId",
conditions = listOf(LicenceCondition(condition = "MockCondition")),
)
},
),
)
val result = mockMvc.performAuthorised("$path?page=1&perPage=15")
it("returns licence condition results") {

result.response.contentAsString.shouldContainJsonKeyValue("$.pagination.page", 1)
result.response.contentAsString.shouldContainJsonKeyValue("$.pagination.totalPages", 2)
val result = mockMvc.performAuthorised(path)
result.response.contentAsString.shouldContain(
"""
"data":{
"hmppsId":"9999/11111A",
"offenderNumber":null,
"licences":[
{
"status":null,
"typeCode":null,
"createdDate":null,
"approvedDate":null,
"updatedDate":null,
"conditions":[
{
"type":null,
"code":null,
"category":null,
"condition":"MockCondition"
}
]
}
]
""".removeWhitespaceAndNewlines(),
)
}
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ internal class GetLicenceConditionServiceTest(
val crn = "Z99999ZZ"
val person = Person(firstName = "Qui-gon", lastName = "Jin", identifiers = Identifiers(deliusCrn = crn))
val licences = listOf(Licence(id = "MockLicenceId"))
var conditions = listOf(LicenceCondition(condition = "MockCondition"))
val conditions = listOf(LicenceCondition(condition = "MockCondition", category = "AP"))

beforeEach {
Mockito.reset(getPersonService)
Expand Down Expand Up @@ -61,7 +61,7 @@ internal class GetLicenceConditionServiceTest(
),
)
val result = getLicenceCondtionService.execute("notfound")
result.data.shouldBe(emptyList())
result.data.licences.shouldBe(emptyList())
result.errors.first().type.shouldBe(UpstreamApiError.Type.ENTITY_NOT_FOUND)
}

Expand All @@ -78,13 +78,13 @@ internal class GetLicenceConditionServiceTest(
),
)
val result = getLicenceCondtionService.execute(hmppsId = hmppsId)
result.data.shouldBe(emptyList())
result.data.licences.shouldBe(emptyList())
result.errors.first().type.shouldBe(UpstreamApiError.Type.ENTITY_NOT_FOUND)
}

it("should return licence condition from gateway") {
val result = getLicenceCondtionService.execute(hmppsId = hmppsId)
result.data.first().conditions.first().condition.shouldBe("MockCondition")
result.data.licences.first().conditions.first().condition.shouldBe("MockCondition")
result.errors.count().shouldBe(0)
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,50 @@ class LicenceConditionSmokeTest : DescribeSpec(
response.body().shouldEqualJson(
"""
{
"data": [
"data": {
"hmppsId": "G2996UX",
"offenderNumber": "A1234AA",
"licences": [
{
"offenderNumber": "A1234AA",
"conditions":
[{
"condition": "Not commit any offence."
}]
"status": "IN_PROGRESS",
"typeCode": "AP",
"createdDate": "2023-11-20T00:00:00Z",
"approvedDate": "2023-11-20T00:00:00Z",
"updatedDate": "2023-11-20T00:00:00Z",
"conditions": [
{
"type": "Bespoke",
"code": null,
"category": null,
"condition": "You should not visit Y"
},
{
"type": "Standard",
"code": "5a105297-dce1-4d18-b9ea-4195b46b7594",
"category": null,
"condition": "Not commit any offence."
},
{
"type": "STANDARD",
"code": "5a105297-dce1-4d18-b9ea-4195b46b7594",
"category": "Residence at a specific place",
"condition": "You must not enter the location X"
},
{
"type": "STANDARD",
"code": "5a105297-dce1-4d18-b9ea-4195b46b7594",
"category": "Residence at a specific place",
"condition": "You must not enter the location X"
},
{
"type": "Standard",
"code": "5a105297-dce1-4d18-b9ea-4195b46b7594",
"category": null,
"condition": "Not commit any offence."
}
]
}
],
"pagination": {
"isLastPage": true,
"count": 1,
"page": 1,
"perPage": 8,
"totalCount": 1,
"totalPages": 1
]
}
}
""".removeWhitespaceAndNewlines(),
Expand Down
Loading