From e7373a26b56e051b7d58882f97e8da48da2874d0 Mon Sep 17 00:00:00 2001 From: Rick Choi Date: Tue, 28 Jan 2025 11:37:27 +0000 Subject: [PATCH] [ESWE-1181] Employer Creation; MN API client, registrar; revise tests (#20) - implemented `MNJobBoardApiClient`, with integration tests - implemented `EmployerRegistrar` - rename test objects' file to `EmployerObjects` - `EmployerService` is incomplete - revise tests of JobsBoardApiClient --- build.gradle.kts | 1 + docker-compose.yml | 3 + .../values.yaml | 1 + helm_deploy/values-dev.yaml | 1 + helm_deploy/values-preprod.yaml | 1 + helm_deploy/values-prod.yaml | 1 + .../employers/domain/EmployerObjects.kt} | 23 +++++- .../integration/IntegrationTestBase.kt | 2 + .../JobsBoardApiWebClientShould.kt | 2 +- .../MNJobBoardApiWebClientShould.kt | 57 +++++++++++++++ .../wiremock/JobsBoardApiMockServer.kt | 2 + .../wiremock/MNJobBoardApiMockServer.kt | 73 +++++++++++++++++++ .../resources/application-test.yml | 6 +- .../config/WebClientConfiguration.kt | 18 ++++- .../application/EmployerRegistrar.kt | 15 +++- .../employers/application/EmployerService.kt | 26 +++++++ .../shared/domain/MNJobBoardApiClient.kt | 8 ++ .../infrastructure/MNJobBoardApiWebClient.kt | 31 ++++++++ .../shared/infrastructure/MNJobBoardData.kt | 38 ++++++++++ src/main/resources/application-dev.yml | 1 + .../EmployerCreationMessageServiceShould.kt | 2 +- .../employers/domain/EmployerObjects.kt} | 2 +- .../JobsBoardApiWebClientShould.kt | 2 +- 23 files changed, 303 insertions(+), 13 deletions(-) rename src/{test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerMother.kt => integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerObjects.kt} (77%) create mode 100644 src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/shared/infrastructure/MNJobBoardApiWebClientShould.kt create mode 100644 src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/wiremock/MNJobBoardApiMockServer.kt create mode 100644 src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/domain/MNJobBoardApiClient.kt create mode 100644 src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/MNJobBoardApiWebClient.kt create mode 100644 src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/MNJobBoardData.kt rename src/{integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerMother.kt => test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerObjects.kt} (98%) diff --git a/build.gradle.kts b/build.gradle.kts index dbdd203..8e19d4b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -45,6 +45,7 @@ testing { implementation("org.apache.commons:commons-compress:1.27.1") } implementation("org.testcontainers:localstack") + implementation("org.jetbrains.kotlin:kotlin-test-junit5") } targets { diff --git a/docker-compose.yml b/docker-compose.yml index 710179d..3f92bc8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,6 +16,9 @@ services: # TODO: Remove this URL and replace with outgoing service URLs - EXAMPLE_URL=http://hmpps-jobs-board-integration-api:8080 - SPRING_PROFILES_ACTIVE=dev,local + - 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= hmpps-auth: image: quay.io/hmpps/hmpps-auth:latest diff --git a/helm_deploy/hmpps-jobs-board-integration-api/values.yaml b/helm_deploy/hmpps-jobs-board-integration-api/values.yaml index d7abdd9..d54b9f5 100644 --- a/helm_deploy/hmpps-jobs-board-integration-api/values.yaml +++ b/helm_deploy/hmpps-jobs-board-integration-api/values.yaml @@ -33,6 +33,7 @@ generic-service: EXAMPLE_API_CLIENT_SECRET: "TEMPLATE_KOTLIN_API_CLIENT_SECRET" API_CLIENT_ID: "SYSTEM_CLIENT_ID" API_CLIENT_SECRET: "SYSTEM_CLIENT_SECRET" + MN_JOBBOARD_API_TOKEN: "MN_JOBBOARD_API_TOKEN" application-insights: APPLICATIONINSIGHTS_CONNECTION_STRING: "APPLICATIONINSIGHTS_CONNECTION_STRING" diff --git a/helm_deploy/values-dev.yaml b/helm_deploy/values-dev.yaml index e716b1d..36fdd5a 100644 --- a/helm_deploy/values-dev.yaml +++ b/helm_deploy/values-dev.yaml @@ -14,6 +14,7 @@ generic-service: # TODO: This should be replaced by a call to a different service, or removed EXAMPLE_API_URL: "https://jobs-board-integration-api-dev.hmpps.service.justice.gov.uk" 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" serviceAccountName: education-skills-work-employment-dev diff --git a/helm_deploy/values-preprod.yaml b/helm_deploy/values-preprod.yaml index 8a2dd22..7f97988 100644 --- a/helm_deploy/values-preprod.yaml +++ b/helm_deploy/values-preprod.yaml @@ -14,6 +14,7 @@ generic-service: # TODO: This should be replaced by a call to a different service, or removed EXAMPLE_API_URL: "https://jobs-board-integration-api-preprod.hmpps.service.justice.gov.uk" API_BASE_URL_JOBSBOARD: "https://jobs-board-api-preprod.hmpps.service.justice.gov.uk" + API_BASE_URL_MNJOBBOARD: "https://preprodservices.sequation.net/sequation-job-api" # URL to be confirmed serviceAccountName: education-skills-work-employment-preprod diff --git a/helm_deploy/values-prod.yaml b/helm_deploy/values-prod.yaml index 56c3c8b..de249b6 100644 --- a/helm_deploy/values-prod.yaml +++ b/helm_deploy/values-prod.yaml @@ -11,6 +11,7 @@ generic-service: # TODO: This should be replaced by a call to a different service, or removed EXAMPLE_API_URL: "https://jobs-board-integration-api.hmpps.service.justice.gov.uk" API_BASE_URL_JOBSBOARD: "https://jobs-board-api.hmpps.service.justice.gov.uk" + API_BASE_URL_MNJOBBOARD: "https://liveservices.sequation.net/sequation-job-api" # URL to be confirmed serviceAccountName: education-skills-work-employment-prod diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerMother.kt b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerObjects.kt similarity index 77% rename from src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerMother.kt rename to src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerObjects.kt index fc500ba..73cdd4e 100644 --- a/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerMother.kt +++ b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerObjects.kt @@ -1,6 +1,8 @@ package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain -object EmployerMother { +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.infrastructure.MNEmployer + +object EmployerObjects { val tesco = Employer( id = "89de6c84-3372-4546-bbc1-9d1dc9ceb354", name = "Tesco", @@ -41,3 +43,22 @@ object EmployerMother { status = "SILVER", ) } + +internal fun Employer.mnEmployer() = MNEmployer( + employerName = name, + employerBio = description, + sectorId = sectorIdMap[sector]!!, + partnerId = statusPartnerIdMap[status]!!, +) + +private val sectorIdMap = mapOf( + "CONSTRUCTION" to 6, + "LOGISTICS" to 8, + "RETAIL" to 7, +) + +private val statusPartnerIdMap = mapOf( + "SILVER" to 3, + "GOLD" to 2, + "KEY_PARTNER" to 1, +) diff --git a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/IntegrationTestBase.kt b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/IntegrationTestBase.kt index c71727d..4027c20 100644 --- a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/IntegrationTestBase.kt +++ b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/IntegrationTestBase.kt @@ -20,6 +20,7 @@ import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock.HmppsAuthApiExtension import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock.HmppsAuthApiExtension.Companion.hmppsAuth import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock.JobsBoardApiExtension +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock.MNJobBoardApiExtension import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.application.DefaultTimeProvider import uk.gov.justice.hmpps.test.kotlin.auth.JwtAuthorisationHelper import java.time.Instant @@ -31,6 +32,7 @@ import java.util.* HmppsAuthApiExtension::class, ExampleApiExtension::class, JobsBoardApiExtension::class, + MNJobBoardApiExtension::class, MockitoExtension::class, ) @SpringBootTest(webEnvironment = RANDOM_PORT) diff --git a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/shared/infrastructure/JobsBoardApiWebClientShould.kt b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/shared/infrastructure/JobsBoardApiWebClientShould.kt index 66e92ae..b4a4385 100644 --- a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/shared/infrastructure/JobsBoardApiWebClientShould.kt +++ b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/shared/infrastructure/JobsBoardApiWebClientShould.kt @@ -5,7 +5,7 @@ import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired -import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.EmployerMother.sainsburys +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.EmployerObjects.sainsburys import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.IntegrationTestBase import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock.HmppsAuthApiExtension.Companion.hmppsAuth import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock.JobsBoardApiExtension.Companion.jobsBoardApi diff --git a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/shared/infrastructure/MNJobBoardApiWebClientShould.kt b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/shared/infrastructure/MNJobBoardApiWebClientShould.kt new file mode 100644 index 0000000..17e20d4 --- /dev/null +++ b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/shared/infrastructure/MNJobBoardApiWebClientShould.kt @@ -0,0 +1,57 @@ +package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.shared.infrastructure + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.EmployerObjects.sainsburys +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.mnEmployer +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.IntegrationTestBase +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock.MNJobBoardApiExtension.Companion.mnJobBoardApi +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.infrastructure.CreatEmployerRequest +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.infrastructure.MNEmployer +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.infrastructure.MNJobBoardApiWebClient +import kotlin.test.assertFailsWith + +class MNJobBoardApiWebClientShould : IntegrationTestBase() { + + @Autowired + private lateinit var apiWebClient: MNJobBoardApiWebClient + + @Nested + @DisplayName("MN JobBoard `POST` /employers") + inner class EmployersPostEndpoint { + private val employer = sainsburys + private val mnEmployer: MNEmployer get() = employer.copy(createdAt = timeProvider.nowAsInstant()).mnEmployer() + + @Test + fun `create employer, with valid details`() { + val expectedEmployer = mnEmployer.copy(id = 1L) + mnJobBoardApi.stubCreateEmployer(mnEmployer, expectedEmployer.id!!) + + val actualEmployer = CreatEmployerRequest.from(mnEmployer).let { apiWebClient.createEmployer(it) } + + assertThat(actualEmployer).isEqualTo(expectedEmployer) + } + + @Test + fun `receive unauthorised error, if API access token is invalid`() { + mnJobBoardApi.stubCreateEmployerUnauthorised() + + val exception = assertFailsWith { + CreatEmployerRequest.from(mnEmployer).let { apiWebClient.createEmployer(it) } + } + + with(exception) { + assertThat(message).contains("Fail to create employer") // reactive throw (ReactiveException) + with(cause!!) { + assertThat(message).contains("Fail to create employer") // actual throw (Exception) + with(cause!!) { + assertThat(message).contains("401 Unauthorized") // cause (401) (WebClientResponseException$Unauthorized) + } + } + } + } + } +} diff --git a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/wiremock/JobsBoardApiMockServer.kt b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/wiremock/JobsBoardApiMockServer.kt index 3075ef8..6bb4cd0 100644 --- a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/wiremock/JobsBoardApiMockServer.kt +++ b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/wiremock/JobsBoardApiMockServer.kt @@ -2,6 +2,7 @@ package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremoc import com.github.tomakehurst.wiremock.WireMockServer import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.containing import com.github.tomakehurst.wiremock.client.WireMock.get import com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching import org.junit.jupiter.api.extension.AfterAllCallback @@ -16,6 +17,7 @@ class JobsBoardApiMockServer : WireMockServer(8092) { fun stubRetrieveEmployer(employer: Employer) { stubFor( get(urlPathMatching(retrieveEmployerPathRegex)) + .withHeader("Authorization", containing("Bearer")) .willReturn( aResponse() .withHeader("Content-Type", "application/json") diff --git a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/wiremock/MNJobBoardApiMockServer.kt b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/wiremock/MNJobBoardApiMockServer.kt new file mode 100644 index 0000000..c343142 --- /dev/null +++ b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/wiremock/MNJobBoardApiMockServer.kt @@ -0,0 +1,73 @@ +package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock + +import com.github.tomakehurst.wiremock.WireMockServer +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.matching +import com.github.tomakehurst.wiremock.client.WireMock.post +import org.junit.jupiter.api.extension.AfterAllCallback +import org.junit.jupiter.api.extension.BeforeAllCallback +import org.junit.jupiter.api.extension.BeforeEachCallback +import org.junit.jupiter.api.extension.ExtensionContext +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.infrastructure.MNEmployer +import java.util.concurrent.atomic.AtomicLong + +private const val EMPLOYERS_ENDPOINT = "/employers" + +class MNJobBoardApiMockServer( + private val nextId: AtomicLong = AtomicLong(1), +) : WireMockServer(8093) { + fun stubCreateEmployer(mnEmployer: MNEmployer) = stubCreateEmployer(mnEmployer, nextId.getAndIncrement()) + fun stubCreateEmployer(mnEmployer: MNEmployer, newId: Long) { + val employerCreated = mnEmployer.copy(id = newId) + stubFor( + post(EMPLOYERS_ENDPOINT) + .withHeader("Authorization", matching("^Bearer .+\$")) + .willReturn( + aResponse() + .withHeader("Content-Type", "application/json") + .withBody(employerCreated.response()), + ), + ) + } + + fun stubCreateEmployerUnauthorised() { + stubFor( + post(EMPLOYERS_ENDPOINT) + .withHeader("Authorization", matching("^Bearer .+\$")) + .willReturn( + aResponse() + .withStatus(401), + ), + ) + } +} + +class MNJobBoardApiExtension : BeforeAllCallback, AfterAllCallback, BeforeEachCallback { + companion object { + @JvmField + val mnJobBoardApi = MNJobBoardApiMockServer() + } + + override fun beforeAll(context: ExtensionContext): Unit = mnJobBoardApi.start() + override fun beforeEach(context: ExtensionContext): Unit = mnJobBoardApi.resetAll() + override fun afterAll(context: ExtensionContext): Unit = mnJobBoardApi.stop() +} + +private fun MNEmployer.response() = """ + { + "message": { + "successCode": "J2047", + "successMessage": "Successfully added employer", + "httpStatusCode": 201 + }, + "responseObject": { + "id": $id, + "employerName": "$employerName", + "employerBio": "$employerBio", + "sectorId": $sectorId, + "partnerId": $partnerId, + "imgName": ${imgName?.let { "\"$it\"" }}, + "path": ${path?.let { "\"$it\"" }} + } + } +""".trimIndent() diff --git a/src/integrationTest/resources/application-test.yml b/src/integrationTest/resources/application-test.yml index a47c60e..fcd9099 100644 --- a/src/integrationTest/resources/application-test.yml +++ b/src/integrationTest/resources/application-test.yml @@ -25,9 +25,13 @@ hmpps.sqs: api: base.url: jobsboard: "http://localhost:8092" + mnjobboard: "http://localhost:8093" client: id: "api-client" secret: "api-client-secret" integration: - enabled: true \ No newline at end of file + enabled: true + +mn.jobboard: + api.token: "mn-api-bearer-token" diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/config/WebClientConfiguration.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/config/WebClientConfiguration.kt index 4d0af64..2748fff 100644 --- a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/config/WebClientConfiguration.kt +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/config/WebClientConfiguration.kt @@ -13,7 +13,6 @@ import java.time.Duration class WebClientConfiguration( @Value("\${example-api.url}") val exampleApiBaseUri: String, @Value("\${hmpps-auth.url}") val hmppsAuthBaseUri: String, - @Value("\${api.base.url.jobsboard}") val jobsboardApiBaseUri: String, @Value("\${api.health-timeout:2s}") val healthTimeout: Duration, @Value("\${api.timeout:20s}") val timeout: Duration, ) { @@ -32,7 +31,18 @@ class WebClientConfiguration( builder.authorisedWebClient(authorizedClientManager, registrationId = "example-api", url = exampleApiBaseUri, timeout) @ConditionalOnIntegrationEnabled - @Bean("jobsBoardWebClient") - fun jobsBoardApiWebClient(authorizedClientManager: OAuth2AuthorizedClientManager, builder: WebClient.Builder): WebClient = - builder.authorisedWebClient(authorizedClientManager, registrationId = "hmpps-jobs-board-api", url = jobsboardApiBaseUri, timeout) + @Bean + fun jobsBoardWebClient( + authorizedClientManager: OAuth2AuthorizedClientManager, + builder: WebClient.Builder, + @Value("\${api.base.url.jobsboard}") jobsboardApiBaseUri: String, + ): WebClient = builder.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() } diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerRegistrar.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerRegistrar.kt index adcb403..a880f66 100644 --- a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerRegistrar.kt +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerRegistrar.kt @@ -6,9 +6,18 @@ import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.Emp @ConditionalOnIntegrationEnabled @Service -class EmployerRegistrar { +class EmployerRegistrar( + private val employerService: EmployerService, +) { fun registerCreation(employer: Employer) { - // TODO implement employer registration to MN job-board API - throw NotImplementedError("Employer-Creation's registration is not yet implemented!") + try { + val mnEmployer = employerService.run { create(convert(employer)) } + assert(mnEmployer.id != null) { "MN Employer ID is missing! employerId=${employer.id}, employerName=${employer.name}" } + employerService.createIdMapping(mnEmployer.id!!, employer.id) + } catch (ex: Exception) { + "Fail to register employer-creation; employerId=${employer.id}, employerName=${employer.name}".let { message -> + throw Exception(message, ex) + } + } } } diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerService.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerService.kt index 14095a3..dffa256 100644 --- a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerService.kt +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerService.kt @@ -4,11 +4,37 @@ import org.springframework.stereotype.Service import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.config.ConditionalOnIntegrationEnabled import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.Employer import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.domain.JobsBoardApiClient +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.domain.MNJobBoardApiClient +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.infrastructure.CreatEmployerRequest +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.infrastructure.MNEmployer @ConditionalOnIntegrationEnabled @Service class EmployerService( private val jobsBoardApiClient: JobsBoardApiClient, + private val mnJobBoardApiClient: MNJobBoardApiClient, ) { fun retrieveById(id: String): Employer? = jobsBoardApiClient.getEmployer(id) + + fun create(mnEmployer: MNEmployer): MNEmployer { + val request = CreatEmployerRequest.from(mnEmployer) + // TODO cater optional fields for employerStatus = KEY_PARTNER (sectorId==2) + return mnJobBoardApiClient.createEmployer(request) + } + + fun convert(employer: Employer) = employer.run { + MNEmployer( + employerName = name, + employerBio = description, + // FIXME translate from employer.sector + sectorId = 1, + // FIXME translate from employer.status + partnerId = 1, + ) + } + + fun createIdMapping(mnId: Long, employerId: String) { + // TODO persist ID mapping + throw NotImplementedError("ID Mapping is not yet implemented") + } } diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/domain/MNJobBoardApiClient.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/domain/MNJobBoardApiClient.kt new file mode 100644 index 0000000..2630f52 --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/domain/MNJobBoardApiClient.kt @@ -0,0 +1,8 @@ +package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.domain + +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.infrastructure.CreatEmployerRequest +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.infrastructure.CreatEmployerResponse + +interface MNJobBoardApiClient { + fun createEmployer(request: CreatEmployerRequest): CreatEmployerResponse +} diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/MNJobBoardApiWebClient.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/MNJobBoardApiWebClient.kt new file mode 100644 index 0000000..5221996 --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/MNJobBoardApiWebClient.kt @@ -0,0 +1,31 @@ +package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.infrastructure + +import org.slf4j.Logger +import org.slf4j.LoggerFactory +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 reactor.core.publisher.Mono +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.config.ConditionalOnIntegrationEnabled +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.domain.MNJobBoardApiClient + +private const val EMPLOYERS_ENDPOINT = "/employers" + +@ConditionalOnIntegrationEnabled +@Service +class MNJobBoardApiWebClient( + @Qualifier("mnJobBoardWebClient") private val mnJobBoardWebClient: WebClient, +) : MNJobBoardApiClient { + companion object { + val log: Logger = LoggerFactory.getLogger(this::class.java) + } + + override fun createEmployer(request: CreatEmployerRequest): CreatEmployerResponse { + log.debug("Creating employer employerName={}", request.employerName) + return mnJobBoardWebClient.post().uri(EMPLOYERS_ENDPOINT).accept(APPLICATION_JSON).retrieve() + .bodyToMono(MNCreatEmployerResponse::class.java) + .onErrorResume { error -> Mono.error(Exception("Fail to create employer", error)) }.block()!! + .responseObject + } +} diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/MNJobBoardData.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/MNJobBoardData.kt new file mode 100644 index 0000000..6733071 --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/MNJobBoardData.kt @@ -0,0 +1,38 @@ +package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.infrastructure + +data class CreatEmployerRequest( + val employerName: String, + val employerBio: String, + val sectorId: Int, + val partnerId: Int, + val imgName: String? = null, + val path: String? = null, +) { + companion object { + fun from(employer: MNEmployer) = + employer.run { CreatEmployerRequest(employerName, employerBio, sectorId, partnerId, imgName, path) } + } +} + +typealias CreatEmployerResponse = MNEmployer + +data class MNCreatEmployerResponse( + val message: MNMessage, + val responseObject: MNEmployer, +) + +data class MNEmployer( + val id: Long? = null, + val employerName: String, + val employerBio: String, + val sectorId: Int, + val partnerId: Int, + val imgName: String? = null, + val path: String? = null, +) + +data class MNMessage( + val successCode: String, + val successMessage: String, + val httpStatusCode: Int, +) diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 3e7156d..20dab86 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -12,6 +12,7 @@ example-api: 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 diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerCreationMessageServiceShould.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerCreationMessageServiceShould.kt index 79dd2e7..8c5eaf3 100644 --- a/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerCreationMessageServiceShould.kt +++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerCreationMessageServiceShould.kt @@ -16,7 +16,7 @@ import org.mockito.kotlin.whenever import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.Employer import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.EmployerEvent import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.EmployerEventType -import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.EmployerMother.sainsburys +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.EmployerObjects.sainsburys import kotlin.test.assertFailsWith @ExtendWith(MockitoExtension::class) diff --git a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerMother.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerObjects.kt similarity index 98% rename from src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerMother.kt rename to src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerObjects.kt index fc500ba..7a10cfa 100644 --- a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerMother.kt +++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerObjects.kt @@ -1,6 +1,6 @@ package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain -object EmployerMother { +object EmployerObjects { val tesco = Employer( id = "89de6c84-3372-4546-bbc1-9d1dc9ceb354", name = "Tesco", diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/JobsBoardApiWebClientShould.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/JobsBoardApiWebClientShould.kt index 5682a89..acbfb93 100644 --- a/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/JobsBoardApiWebClientShould.kt +++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/JobsBoardApiWebClientShould.kt @@ -5,7 +5,7 @@ import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.mockito.junit.jupiter.MockitoExtension -import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.EmployerMother.sainsburys +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.EmployerObjects.sainsburys @ExtendWith(MockitoExtension::class) class JobsBoardApiWebClientShould : JobsBoardApiWebClientTestBase() {