Skip to content

Commit

Permalink
[ESWE-1181] Fix API clients, JPA auditing (#25)
Browse files Browse the repository at this point in the history
Fix API clients
1) MN API client: missing body
2) JB API client: scopes' separator
3) Error & debug logging
4) Rename container name of local `hmpps-auth` (avoid conflict at
Docker)

Fix JPA auditing
- enable JPA auditing for `@CreatedDate` and `@LastModifiedDate`
  • Loading branch information
rickchoijd authored Jan 30, 2025
1 parent 80d2ffa commit 8c256ff
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 8 deletions.
4 changes: 3 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ services:
- API_BASE_URL_JOBSBOARD=https://jobs-board-api-dev.hmpps.service.justice.gov.uk
- API_BASE_URL_MNJOBBOARD=https://testservices.sequation.net/sequation-job-api
- MN_JOBBOARD_API_TOKEN=<OVERRIDE_THIS>
- API_CLIENT_ID=<OVERRIDE_THIS>
- API_CLIENT_SECRET=<OVERRIDE_THIS>

hmpps-auth:
image: quay.io/hmpps/hmpps-auth:latest
networks:
- hmpps
container_name: hmpps-auth
container_name: integration-hmpps-auth
ports:
- "8090:8080"
healthcheck:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.config

import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaAuditing

@Configuration
@EnableJpaAuditing
class JpaConfig
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.config

import io.netty.handler.logging.LogLevel
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.client.reactive.ReactorClientHttpConnector
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager
import org.springframework.web.reactive.function.client.WebClient
import reactor.netty.http.client.HttpClient
import reactor.netty.transport.logging.AdvancedByteBufFormat
import uk.gov.justice.hmpps.kotlin.auth.authorisedWebClient
import uk.gov.justice.hmpps.kotlin.auth.healthWebClient
import java.time.Duration
Expand All @@ -15,6 +19,7 @@ class WebClientConfiguration(
@Value("\${hmpps-auth.url}") val hmppsAuthBaseUri: String,
@Value("\${api.health-timeout:2s}") val healthTimeout: Duration,
@Value("\${api.timeout:20s}") val timeout: Duration,
@Value("\${api.client.logging.enabled:false}") val clientLogging: Boolean,
) {
// HMPPS Auth health ping is required if your service calls HMPPS Auth to get a token to call other services
// TODO: Remove the health ping if no call outs to other services are made
Expand All @@ -36,13 +41,30 @@ class WebClientConfiguration(
authorizedClientManager: OAuth2AuthorizedClientManager,
builder: WebClient.Builder,
@Value("\${api.base.url.jobsboard}") jobsboardApiBaseUri: String,
): WebClient = builder.authorisedWebClient(authorizedClientManager, registrationId = "hmpps-jobs-board-api", url = jobsboardApiBaseUri, timeout)
): WebClient = builder.apply {
if (clientLogging) it.clientConnector(clientConnectorWithLogging())
}.authorisedWebClient(
authorizedClientManager,
registrationId = "hmpps-jobs-board-api",
url = jobsboardApiBaseUri,
timeout,
)

@ConditionalOnIntegrationEnabled
@Bean
fun mnJobBoardWebClient(
builder: WebClient.Builder,
@Value("\${api.base.url.mnjobboard}") mnjobboardApiBaseUri: String,
@Value("\${mn.jobboard.api.token}") mnJobBoardToken: String,
): WebClient = builder.defaultHeader("Authorization", "Bearer $mnJobBoardToken").baseUrl(mnjobboardApiBaseUri).build()
): WebClient = builder.defaultHeader("Authorization", "Bearer $mnJobBoardToken").baseUrl(mnjobboardApiBaseUri).apply {
if (clientLogging) it.clientConnector(clientConnectorWithLogging())
}.build()

private fun clientConnectorWithLogging() = ReactorClientHttpConnector(httpClientWireTapLogging())

private fun httpClientWireTapLogging() = HttpClient.create().wiretap(
"reactor.netty.http.client.HttpClient",
LogLevel.DEBUG,
AdvancedByteBufFormat.TEXTUAL,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ class JobsBoardApiWebClient(
return jobsBoardWebClient
.get().uri("/employers/{id}", id).accept(APPLICATION_JSON).retrieve()
.bodyToMono(GetEmployerResponse::class.java)
.onErrorResume(WebClientResponseException.NotFound::class.java) {
log.debug("Employer not found. employerId={}", id)
.onErrorResume(WebClientResponseException.NotFound::class.java) { error ->
val errorResponse = if (error is WebClientResponseException) error.responseBodyAsString else null
log.warn("Employer not found. employerId={}; errorResponse={}", id, errorResponse)
Mono.empty()
}.block()?.employer()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.http.MediaType.APPLICATION_JSON
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.WebClientResponseException
import reactor.core.publisher.Mono
import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.config.ConditionalOnIntegrationEnabled
import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.domain.MNJobBoardApiClient
Expand All @@ -23,9 +24,15 @@ class MNJobBoardApiWebClient(

override fun createEmployer(request: CreatEmployerRequest): CreatEmployerResponse {
log.debug("Creating employer employerName={}", request.employerName)
return mnJobBoardWebClient.post().uri(EMPLOYERS_ENDPOINT).accept(APPLICATION_JSON).retrieve()
log.trace("Create employer request={}", request)
return mnJobBoardWebClient.post().uri(EMPLOYERS_ENDPOINT)
.accept(APPLICATION_JSON).body(Mono.just(request), request.javaClass)
.retrieve()
.bodyToMono(MNCreatEmployerResponse::class.java)
.onErrorResume { error -> Mono.error(Exception("Fail to create employer", error)) }.block()!!
.onErrorResume { error ->
val errorResponse = if (error is WebClientResponseException) error.responseBodyAsString else null
Mono.error(Exception("Fail to create employer! errorResponse=$errorResponse", error))
}.block()!!
.responseObject
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"properties": [
{
"name": "api.base.url.jobsboard",
"type": "java.lang.String",
"description": "Base URL for Jobs Board APIs"
},
{
"name": "api.base.url.mnjobboard",
"type": "java.lang.String",
"description": "Base URL for MN Job Board APIs"
},
{
"name": "api.integration.enabled",
"type": "java.lang.String",
"description": "Feature flag to switch on/off JobsBoard-to-MN-Job-Board Integration functions"
},
{
"name": "api.client.logging.enabled",
"type": "java.lang.String",
"description": "Enabling client logging at API Web Client; disabled by default"
}
] }
8 changes: 7 additions & 1 deletion src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ api:
base.url:
jobsboard: "https://jobs-board-api-dev.hmpps.service.justice.gov.uk"
mnjobboard: "https://testservices.sequation.net/sequation-job-api"

integration:
enabled: false
client.logging:
enabled: false

spring:
datasource:
Expand All @@ -25,3 +26,8 @@ spring:
flyway:
user: 'job-board-intg'
password: 'job-board-intg'

logging.level:
uk.gov.justice.digital.hmpps.jobsboardintegrationapi:
shared.infrastructure: DEBUG
reactor.netty.http.client: INFO

0 comments on commit 8c256ff

Please sign in to comment.