Skip to content

Commit fcaa854

Browse files
Merge branch 'main' into VB-6479-add-sort-order-to-paged-results
2 parents c2e2cef + 01126a1 commit fcaa854

37 files changed

+326
-190
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,4 @@ sonar-project.properties
7676
.env
7777

7878
#Perf tests
79-
./src/gatling/resources/data/
79+
src/gatling/resources/data/

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# hmpps-personal-relationships-api
22

3-
[![repo standards badge](https://img.shields.io/badge/endpoint.svg?&style=flat&logo=github&url=https%3A%2F%2Foperations-engineering-reports.cloud-platform.service.justice.gov.uk%2Fapi%2Fv1%2Fcompliant_public_repositories%2Fhmpps-personal-relationships-api)](https://operations-engineering-reports.cloud-platform.service.justice.gov.uk/public-report/hmpps-personal-relationships-api "Link to report")
3+
[![Ministry of Justice Repository Compliance Badge](https://github-community.service.justice.gov.uk/repository-standards/api/hmpps-personal-relationships-api/badge)](https://github-community.service.justice.gov.uk/repository-standards/hmpps-personal-relationships-api)
44
[![Docker Repository on ghcr](https://img.shields.io/badge/ghcr.io-repository-2496ED.svg?logo=docker)](https://ghcr.io/ministryofjustice/hmpps-personal-relationships-api)
55
[![API docs](https://img.shields.io/badge/API_docs_-view-85EA2D.svg?logo=swagger)](https://personal-relationships-api-dev.hmpps.service.justice.gov.uk/swagger-ui/index.html)
66
[![GitHub Actions Pipeline](https://github.com/ministryofjustice/hmpps-personal-relationships-api/actions/workflows/pipeline.yml/badge.svg)](https://github.com/ministryofjustice/hmpps-personal-relationships-api/actions/workflows/pipeline.yml)

build.gradle.kts

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
22
import org.openapitools.generator.gradle.plugin.tasks.GenerateTask
33

44
plugins {
5-
id("uk.gov.justice.hmpps.gradle-spring-boot") version "10.0.3"
5+
id("uk.gov.justice.hmpps.gradle-spring-boot") version "10.0.4"
66
id("org.openapi.generator") version "7.20.0"
7-
id("io.gatling.gradle") version "3.14.9"
8-
kotlin("plugin.spring") version "2.3.0"
9-
kotlin("plugin.jpa") version "2.3.0"
7+
id("io.gatling.gradle") version "3.15.0"
8+
kotlin("plugin.spring") version "2.3.10"
9+
kotlin("plugin.jpa") version "2.3.10"
1010
}
1111

1212
allOpen {
@@ -43,48 +43,47 @@ dependencyCheck {
4343
dependencies {
4444
// Spring boot dependencies
4545

46-
implementation("uk.gov.justice.service.hmpps:hmpps-kotlin-spring-boot-starter:2.0.0")
46+
implementation("uk.gov.justice.service.hmpps:hmpps-kotlin-spring-boot-starter:2.0.2")
4747
implementation("org.springframework.boot:spring-boot-starter-webclient")
4848
implementation("org.springframework.boot:spring-boot-starter-webflux")
4949
implementation("org.springframework.boot:spring-boot-starter-flyway")
5050
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
51-
implementation("uk.gov.justice.service.hmpps:hmpps-sqs-spring-boot-starter:6.0.0")
52-
implementation("io.sentry:sentry-spring-boot-4-starter:8.31.0")
51+
implementation("uk.gov.justice.service.hmpps:hmpps-sqs-spring-boot-starter:6.0.1")
52+
implementation("io.sentry:sentry-spring-boot-4-starter:8.33.0")
5353
implementation("org.springframework.boot:spring-boot-starter-validation")
5454
implementation("org.openapitools:jackson-databind-nullable:0.2.9")
5555
implementation("org.apache.logging.log4j:log4j-api:2.25.3")
56-
implementation("io.github.cdimascio:dotenv-kotlin:6.4.1")
56+
implementation("io.github.cdimascio:dotenv-kotlin:6.5.1")
5757

5858
// CSV dependencies
59-
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-csv:2.20.1")
59+
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-csv:2.21.1")
6060

6161
// Database dependencies
6262
runtimeOnly("org.flywaydb:flyway-database-postgresql")
63-
runtimeOnly("org.postgresql:postgresql:42.7.8")
63+
runtimeOnly("org.postgresql:postgresql:42.7.10")
6464
implementation("org.hibernate.orm:hibernate-envers")
6565
implementation("org.springframework.data:spring-data-envers")
6666

6767
// OpenAPI
68-
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:3.0.1")
68+
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:3.0.2")
6969

70-
implementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations:2.21.0")
70+
implementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations:2.25.0")
7171
implementation("org.springframework.boot:spring-boot-jackson2")
72-
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.21.0")
72+
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.21.1")
7373

7474
// Test dependencies
75-
testImplementation("uk.gov.justice.service.hmpps:hmpps-kotlin-spring-boot-starter-test:2.0.0")
75+
testImplementation("uk.gov.justice.service.hmpps:hmpps-kotlin-spring-boot-starter-test:2.0.2")
7676
testImplementation("org.springframework.boot:spring-boot-starter-webclient-test")
77-
7877
testImplementation("org.springframework.boot:spring-boot-starter-webflux-test")
7978
testImplementation("org.wiremock:wiremock-standalone:3.13.2")
80-
testImplementation("io.swagger.parser.v3:swagger-parser:2.1.37") {
81-
testImplementation("org.mockito:mockito-inline:5.2.0")
79+
testImplementation("io.swagger.parser.v3:swagger-parser:2.1.39") {
8280
exclude(group = "io.swagger.core.v3")
8381
}
82+
testImplementation("org.mockito:mockito-inline:5.2.0")
8483
testImplementation("org.testcontainers:testcontainers-postgresql:2.0.3")
8584
testImplementation("org.testcontainers:testcontainers-localstack:2.0.3")
8685
testImplementation("org.awaitility:awaitility-kotlin:4.3.0")
87-
testImplementation("io.opentelemetry:opentelemetry-sdk-testing:1.56.0")
86+
testImplementation("io.opentelemetry:opentelemetry-sdk-testing:1.59.0")
8887
}
8988

9089
tasks {

gradle/wrapper/gradle-wrapper.jar

2.66 KB
Binary file not shown.

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

gradlew

Lines changed: 7 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gradlew.bat

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/main/kotlin/uk/gov/justice/digital/hmpps/personalrelationships/config/HmppsContactsApiExceptionHandler.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice
2222
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException
2323
import org.springframework.web.servlet.resource.NoResourceFoundException
2424
import uk.gov.justice.digital.hmpps.personalrelationships.exception.DuplicateEmailException
25+
import uk.gov.justice.digital.hmpps.personalrelationships.exception.DuplicateIdentityDocumentException
2526
import uk.gov.justice.digital.hmpps.personalrelationships.exception.DuplicatePersonException
2627
import uk.gov.justice.digital.hmpps.personalrelationships.exception.DuplicateRelationshipException
2728
import uk.gov.justice.digital.hmpps.personalrelationships.exception.InvalidReferenceCodeGroupException
@@ -149,7 +150,7 @@ class HmppsContactsApiExceptionHandler {
149150
),
150151
)
151152

152-
@ExceptionHandler(DuplicatePersonException::class, DuplicateEmailException::class, DuplicateRelationshipException::class)
153+
@ExceptionHandler(DuplicatePersonException::class, DuplicateEmailException::class, DuplicateRelationshipException::class, DuplicateIdentityDocumentException::class)
153154
fun handleDuplicateException(e: RuntimeException): ResponseEntity<ErrorResponse> = ResponseEntity
154155
.status(CONFLICT)
155156
.body(
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package uk.gov.justice.digital.hmpps.personalrelationships.exception
2+
3+
class DuplicateIdentityDocumentException(val type: String, val value: String) : RuntimeException("Contact already has an identity document matching type \"$type\" and value \"$value\"")

src/main/kotlin/uk/gov/justice/digital/hmpps/personalrelationships/service/ContactIdentityService.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import jakarta.validation.ValidationException
66
import org.springframework.stereotype.Service
77
import uk.gov.justice.digital.hmpps.personalrelationships.config.User
88
import uk.gov.justice.digital.hmpps.personalrelationships.entity.ContactIdentityEntity
9+
import uk.gov.justice.digital.hmpps.personalrelationships.exception.DuplicateIdentityDocumentException
910
import uk.gov.justice.digital.hmpps.personalrelationships.mapping.toModel
1011
import uk.gov.justice.digital.hmpps.personalrelationships.model.ReferenceCodeGroup
1112
import uk.gov.justice.digital.hmpps.personalrelationships.model.request.identity.CreateIdentityRequest
1213
import uk.gov.justice.digital.hmpps.personalrelationships.model.request.identity.CreateMultipleIdentitiesRequest
14+
import uk.gov.justice.digital.hmpps.personalrelationships.model.request.identity.IdentityDocument
1315
import uk.gov.justice.digital.hmpps.personalrelationships.model.request.identity.UpdateIdentityRequest
1416
import uk.gov.justice.digital.hmpps.personalrelationships.model.response.ContactIdentityDetails
1517
import uk.gov.justice.digital.hmpps.personalrelationships.model.response.ReferenceCode
@@ -30,6 +32,8 @@ class ContactIdentityService(
3032
@Transactional
3133
fun create(contactId: Long, request: CreateIdentityRequest, user: User): ContactIdentityDetails {
3234
validateContactExists(contactId)
35+
validateDuplicateIdentityDocuments(contactId, listOf(IdentityDocument(identityType = request.identityType, identityValue = request.identityValue, issuingAuthority = request.issuingAuthority)))
36+
3337
return createAContactIdentity(
3438
contactId,
3539
request.identityType,
@@ -42,6 +46,7 @@ class ContactIdentityService(
4246
@Transactional
4347
fun createMultiple(contactId: Long, request: CreateMultipleIdentitiesRequest, user: User): List<ContactIdentityDetails> {
4448
validateContactExists(contactId)
49+
validateDuplicateIdentityDocuments(contactId, request.identities)
4550
return request.identities.map {
4651
createAContactIdentity(
4752
contactId,
@@ -126,6 +131,31 @@ class ContactIdentityService(
126131
.orElseThrow { EntityNotFoundException("Contact ($contactId) not found") }
127132
}
128133

134+
private fun validateDuplicateIdentityDocuments(contactId: Long, identities: List<IdentityDocument>) {
135+
val requestedIdentities = identities.map { it.identityType to it.identityValue }
136+
137+
val duplicateInRequest = requestedIdentities
138+
.groupingBy { it }
139+
.eachCount()
140+
.entries
141+
.firstOrNull { it.value > 1 }
142+
?.key
143+
144+
if (duplicateInRequest != null) {
145+
throw DuplicateIdentityDocumentException(duplicateInRequest.first, duplicateInRequest.second)
146+
}
147+
148+
val existingIdentities = contactIdentityRepository.findByContactId(contactId)
149+
.map { it.identityType to it.identityValue }
150+
.toSet()
151+
152+
val duplicateExisting = requestedIdentities.firstOrNull { it in existingIdentities }
153+
154+
if (duplicateExisting != null) {
155+
throw DuplicateIdentityDocumentException(duplicateExisting.first, duplicateExisting.second)
156+
}
157+
}
158+
129159
private fun ContactIdentityEntity.toDomainWithType(
130160
type: ReferenceCode,
131161
) = ContactIdentityDetails(

0 commit comments

Comments
 (0)