Skip to content

Commit

Permalink
CDPS-1167: Implementing proxy request to update smoker status (#17)
Browse files Browse the repository at this point in the history
* CDPS-1167: Implementing proxy request to update smoker status

* CDPS-1167: Addressing comments
  • Loading branch information
brightonsbox authored Feb 7, 2025
1 parent a314845 commit 1337bbd
Show file tree
Hide file tree
Showing 59 changed files with 979 additions and 255 deletions.
1 change: 1 addition & 0 deletions helm_deploy/values-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ generic-service:
APPLICATIONINSIGHTS_CONFIGURATION_FILE: "applicationinsights.dev.json"
API_HMPPS_AUTH_BASE_URL: "https://sign-in-dev.hmpps.service.justice.gov.uk/auth"
API_PRISONER_SEARCH_BASE_URL: "https://prisoner-search-dev.prison.service.justice.gov.uk"
PRISON_API_BASE_URL: "https://prison-api-dev.prison.service.justice.gov.uk"

scheduledDowntime:
enabled: true
Expand Down
1 change: 1 addition & 0 deletions helm_deploy/values-preprod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ generic-service:
APPLICATIONINSIGHTS_CONFIGURATION_FILE: "applicationinsights.dev.json"
API_HMPPS_AUTH_BASE_URL: "https://sign-in-preprod.hmpps.service.justice.gov.uk/auth"
API_PRISONER_SEARCH_BASE_URL: "https://prisoner-search-preprod.prison.service.justice.gov.uk"
PRISON_API_BASE_URL: "https://prison-api-preprod.prison.service.justice.gov.uk"

# CloudPlatform AlertManager receiver to route prometheus alerts to slack
# See https://user-guide.cloud-platform.service.justice.gov.uk/documentation/monitoring-an-app/how-to-create-alarms.html#creating-your-own-custom-alerts
Expand Down
1 change: 1 addition & 0 deletions helm_deploy/values-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ generic-service:
env:
API_HMPPS_AUTH_BASE_URL: "https://sign-in.hmpps.service.justice.gov.uk/auth"
API_PRISONER_SEARCH_BASE_URL: "https://prisoner-search.prison.service.justice.gov.uk"
PRISON_API_BASE_URL: "https://prison-api.prison.service.justice.gov.uk"

postgresDatabaseRestore:
enabled: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package uk.gov.justice.digital.hmpps.healthandmedication.annotation

import jakarta.validation.Constraint
import jakarta.validation.Payload
import uk.gov.justice.digital.hmpps.healthandmedication.validator.ReferenceDataValidator
import uk.gov.justice.digital.hmpps.healthandmedication.validator.ReferenceDataListValidator
import kotlin.reflect.KClass

@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Constraint(validatedBy = [ReferenceDataValidator::class])
@Constraint(validatedBy = [ReferenceDataListValidator::class])
annotation class ReferenceDataCode(
val domains: Array<String> = [],
val message: String = "The value must be a reference domain code id of the correct domain, null, or Undefined.",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package uk.gov.justice.digital.hmpps.healthandmedication.annotation

import jakarta.validation.Constraint
import jakarta.validation.Payload
import uk.gov.justice.digital.hmpps.healthandmedication.validator.ReferenceDataListValidator
import kotlin.reflect.KClass

@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Constraint(validatedBy = [ReferenceDataListValidator::class])
annotation class ReferenceDataListValidation(
val domains: Array<String> = [],
val message: String = "The supplied array must either be empty or contain reference data codes of the correct domain.",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Payload>> = [],
)
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import kotlin.reflect.KClass
@Constraint(validatedBy = [ReferenceDataValidator::class])
annotation class ReferenceDataValidation(
val domains: Array<String> = [],
val message: String = "The supplied array must either be empty or contain reference data codes of the correct domain.",
val message: String = "The supplied code must either be null or match a valid reference data code of the correct domain.",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Payload>> = [],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package uk.gov.justice.digital.hmpps.healthandmedication.client.prisonapi

import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.client.WebClient
import uk.gov.justice.digital.hmpps.healthandmedication.client.prisonapi.request.PrisonApiSmokerStatusUpdate
import uk.gov.justice.digital.hmpps.healthandmedication.config.DownstreamServiceException

@Component
class PrisonApiClient(@Qualifier("prisonApiWebClient") private val webClient: WebClient) {
fun updateSmokerStatus(offenderNo: String, updateSmokerStatus: PrisonApiSmokerStatusUpdate) = try {
webClient
.put()
.uri("/api/offenders/{offenderNo}/smoker", offenderNo)
.bodyValue(updateSmokerStatus)
.retrieve()
.toBodilessEntity()
.block()
} catch (e: Exception) {
throw DownstreamServiceException("Update smoker status request failed", e)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package uk.gov.justice.digital.hmpps.healthandmedication.client.prisonapi.request

import io.swagger.v3.oas.annotations.media.Schema

enum class PrisonApiSmokerStatus {
Y,
N,
V,
}

@Schema(description = "Update to prisoner's smoker status")
data class PrisonApiSmokerStatusUpdate(
@Schema(
description = "The smoker status code ('Y' for 'Yes', 'N' for 'No', 'V' for 'Vaper/NRT Only')",
example = "Y",
allowableValues = ["Y", "N", "V"],
required = true,
nullable = true,
)
val smokerStatus: PrisonApiSmokerStatus?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.WebClientResponseException.NotFound
import uk.gov.justice.digital.hmpps.healthandmedication.client.prisonersearch.dto.PrisonerDto
import uk.gov.justice.digital.hmpps.healthandmedication.client.prisonersearch.dto.PrisonerSearchResultDto
import uk.gov.justice.digital.hmpps.healthandmedication.client.prisonersearch.response.PrisonerDto
import uk.gov.justice.digital.hmpps.healthandmedication.client.prisonersearch.response.PrisonerSearchResultDto
import uk.gov.justice.digital.hmpps.healthandmedication.config.DownstreamServiceException

@Component
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package uk.gov.justice.digital.hmpps.healthandmedication.client.prisonersearch.dto
package uk.gov.justice.digital.hmpps.healthandmedication.client.prisonersearch.response

data class PrisonerDto(
val prisonerNumber: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package uk.gov.justice.digital.hmpps.healthandmedication.client.prisonersearch.dto
package uk.gov.justice.digital.hmpps.healthandmedication.client.prisonersearch.response

data class PrisonerSearchResultDto(
val content: List<PrisonerDto>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ class HealthAndMedicationExceptionHandler {
class HealthAndMedicationDataNotFoundException(prisonerNumber: String) : Exception("No data for '$prisonerNumber'")
class ReferenceDataDomainNotFoundException(code: String) : Exception("No data for domain '$code'")
class ReferenceDataCodeNotFoundException(code: String, domain: String) : Exception("No data for code '$code' in domain '$domain'")

class GenericNotFoundException(message: String) : Exception(message)
class IllegalFieldHistoryException(prisonerNumber: String) : Exception("Cannot update field history for prisoner: '$prisonerNumber'")

class DownstreamServiceException(message: String, cause: Throwable) : Exception(message, cause)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package uk.gov.justice.digital.hmpps.personintegrationapi.config

import org.springframework.http.HttpMethod
import org.springframework.http.RequestEntity
import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest
import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequestEntityConverter
import org.springframework.util.MultiValueMap
import java.util.Objects

@Suppress("UNCHECKED_CAST")
class UserEnhancedOAuth2ClientCredentialGrantRequestConverter : OAuth2ClientCredentialsGrantRequestEntityConverter() {

fun enhanceWithUsername(
grantRequest: OAuth2ClientCredentialsGrantRequest?,
username: String?,
): RequestEntity<Any> {
val request = super.convert(grantRequest)
val headers = request.headers
val body = Objects.requireNonNull(request).body
val formParameters = body as MultiValueMap<String, Any>
if (username != null) {
formParameters.add("username", username)
}
return RequestEntity(formParameters, headers, HttpMethod.POST, request.url)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,19 @@ package uk.gov.justice.digital.hmpps.healthandmedication.config
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager
import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService
import org.springframework.security.oauth2.client.endpoint.DefaultClientCredentialsTokenResponseClient
import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository
import org.springframework.web.context.annotation.RequestScope
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.WebClient.Builder
import uk.gov.justice.digital.hmpps.personintegrationapi.config.UserEnhancedOAuth2ClientCredentialGrantRequestConverter
import uk.gov.justice.hmpps.kotlin.auth.authorisedWebClient
import uk.gov.justice.hmpps.kotlin.auth.healthWebClient
import java.time.Duration
Expand All @@ -18,18 +28,60 @@ class WebClientConfiguration(
@Value("\${api.prisoner-search.base-url}") private val prisonerSearchBaseUri: String,
@Value("\${api.prisoner-search.timeout:30s}") private val prisonerSearchTimeout: Duration,
@Value("\${api.prisoner-search.health-timeout:20s}") private val prisonerSearchHealthTimeout: Duration,

@Value("\${api.prison-api.base-url}") private val prisonApiBaseUri: String,
@Value("\${api.prison-api.timeout:30s}") private val prisonApiTimeout: Duration,
@Value("\${api.prison-api.health-timeout:20s}") private val prisonApiHealthTimeout: Duration,
) {
@Bean
fun hmppsAuthHealthWebClient(builder: Builder): WebClient = builder.healthWebClient(hmppsAuthBaseUri, hmppsAuthHealthTimeout)

@Bean
fun prisonerSearchHealthWebClient(builder: Builder) = builder.healthWebClient(prisonerSearchBaseUri, prisonerSearchHealthTimeout)

@Bean
fun prisonApiHealthWebClient(builder: Builder): WebClient = builder.healthWebClient(prisonApiBaseUri, prisonApiHealthTimeout)

@Bean
fun prisonerSearchWebClient(authorizedClientManager: OAuth2AuthorizedClientManager, builder: Builder) = builder.authorisedWebClient(
authorizedClientManager,
"hmpps-health-and-medication-api",
prisonerSearchBaseUri,
prisonerSearchTimeout,
)

@Bean
@RequestScope
fun prisonApiWebClient(
clientRegistrationRepository: ClientRegistrationRepository,
builder: Builder,
) = builder.authorisedWebClient(
authorizedClientManagerUserEnhanced(clientRegistrationRepository),
"hmpps-health-and-medication-api",
prisonApiBaseUri,
prisonApiTimeout,
)

private fun authorizedClientManagerUserEnhanced(clients: ClientRegistrationRepository?): OAuth2AuthorizedClientManager {
val service: OAuth2AuthorizedClientService = InMemoryOAuth2AuthorizedClientService(clients)
val manager = AuthorizedClientServiceOAuth2AuthorizedClientManager(clients, service)

val defaultClientCredentialsTokenResponseClient = DefaultClientCredentialsTokenResponseClient()
val authentication = SecurityContextHolder.getContext().authentication
defaultClientCredentialsTokenResponseClient.setRequestEntityConverter { grantRequest: OAuth2ClientCredentialsGrantRequest ->
val converter = UserEnhancedOAuth2ClientCredentialGrantRequestConverter()
converter.enhanceWithUsername(grantRequest, authentication.name)
}

val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials { clientCredentialsGrantBuilder: OAuth2AuthorizedClientProviderBuilder.ClientCredentialsGrantBuilder ->
clientCredentialsGrantBuilder.accessTokenResponseClient(
defaultClientCredentialsTokenResponseClient,
)
}
.build()

manager.setAuthorizedClientProvider(authorizedClientProvider)
return manager
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ import uk.gov.justice.hmpps.kotlin.health.HealthPingCheck

@Component("hmppsAuth")
class HealthPingCheck(@Qualifier("hmppsAuthHealthWebClient") webClient: WebClient) : HealthPingCheck(webClient)

@Component("prisonerSearch")
class PrisonerSearchHealthPing(@Qualifier("prisonerSearchHealthWebClient") webClient: WebClient) : HealthPingCheck(webClient)

@Component("prisonApi")
class PrisonApiHealthPing(@Qualifier("prisonApiHealthWebClient") webClient: WebClient) : HealthPingCheck(webClient)
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import jakarta.persistence.OneToMany
import jakarta.persistence.Table
import org.hibernate.Hibernate
import org.hibernate.annotations.SortNatural
import uk.gov.justice.digital.hmpps.healthandmedication.dto.response.DietAndAllergyDto
import uk.gov.justice.digital.hmpps.healthandmedication.dto.response.HealthDto
import uk.gov.justice.digital.hmpps.healthandmedication.dto.response.ReferenceDataSelection
import uk.gov.justice.digital.hmpps.healthandmedication.dto.response.ValueWithMetadata
import uk.gov.justice.digital.hmpps.healthandmedication.enums.HealthAndMedicationField
import uk.gov.justice.digital.hmpps.healthandmedication.enums.HealthAndMedicationField.FOOD_ALLERGY
import uk.gov.justice.digital.hmpps.healthandmedication.enums.HealthAndMedicationField.MEDICAL_DIET
import uk.gov.justice.digital.hmpps.healthandmedication.enums.HealthAndMedicationField.PERSONALISED_DIET
import uk.gov.justice.digital.hmpps.healthandmedication.mapper.toSimpleDto
import uk.gov.justice.digital.hmpps.healthandmedication.resource.dto.response.DietAndAllergyResponse
import uk.gov.justice.digital.hmpps.healthandmedication.resource.dto.response.HealthAndMedicationResponse
import uk.gov.justice.digital.hmpps.healthandmedication.resource.dto.response.ReferenceDataSelection
import uk.gov.justice.digital.hmpps.healthandmedication.resource.dto.response.ValueWithMetadata
import java.time.ZonedDateTime
import java.util.SortedSet
import kotlin.reflect.KMutableProperty0
Expand Down Expand Up @@ -56,9 +56,9 @@ class PrisonerHealth(
PERSONALISED_DIET to ::personalisedDietaryRequirements,
)

fun toHealthDto(): HealthDto = HealthDto(toDietAndAllergyDto())
fun toHealthDto(): HealthAndMedicationResponse = HealthAndMedicationResponse(toDietAndAllergyDto())

fun toDietAndAllergyDto(): DietAndAllergyDto = DietAndAllergyDto(
fun toDietAndAllergyDto(): DietAndAllergyResponse = DietAndAllergyResponse(
foodAllergies = getReferenceDataListValueWithMetadata(
foodAllergies,
{ allergies ->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package uk.gov.justice.digital.hmpps.healthandmedication.mapper

import uk.gov.justice.digital.hmpps.healthandmedication.dto.ReferenceDataCodeDto
import uk.gov.justice.digital.hmpps.healthandmedication.dto.ReferenceDataSimpleDto
import uk.gov.justice.digital.hmpps.healthandmedication.jpa.ReferenceDataCode
import uk.gov.justice.digital.hmpps.healthandmedication.resource.dto.ReferenceDataCodeDto
import uk.gov.justice.digital.hmpps.healthandmedication.resource.dto.ReferenceDataSimpleDto
import java.time.ZonedDateTime

fun ReferenceDataCode.toDto(): ReferenceDataCodeDto = ReferenceDataCodeDto(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package uk.gov.justice.digital.hmpps.healthandmedication.mapper

import uk.gov.justice.digital.hmpps.healthandmedication.dto.ReferenceDataDomainDto
import uk.gov.justice.digital.hmpps.healthandmedication.jpa.ReferenceDataDomain
import uk.gov.justice.digital.hmpps.healthandmedication.resource.dto.ReferenceDataDomainDto
import java.time.ZonedDateTime

fun ReferenceDataDomain.toDto(): ReferenceDataDomainDto = ReferenceDataDomainDto(
Expand Down
Loading

0 comments on commit 1337bbd

Please sign in to comment.