diff --git a/bpdm-cleaning-service-dummy/pom.xml b/bpdm-cleaning-service-dummy/pom.xml index b79f8592e..f8c5b56d3 100644 --- a/bpdm-cleaning-service-dummy/pom.xml +++ b/bpdm-cleaning-service-dummy/pom.xml @@ -123,6 +123,22 @@ + + com.github.tomakehurst + wiremock-jre8-standalone + test + + + com.ninja-squad + springmockk + test + + + org.assertj + assertj-core + test + + org.assertj assertj-core diff --git a/bpdm-cleaning-service-dummy/src/main/kotlin/org/eclipse/tractusx/bpdm/cleaning/Application.kt b/bpdm-cleaning-service-dummy/src/main/kotlin/org/eclipse/tractusx/bpdm/cleaning/Application.kt index 30730dfd5..0c84b7bf8 100644 --- a/bpdm-cleaning-service-dummy/src/main/kotlin/org/eclipse/tractusx/bpdm/cleaning/Application.kt +++ b/bpdm-cleaning-service-dummy/src/main/kotlin/org/eclipse/tractusx/bpdm/cleaning/Application.kt @@ -23,9 +23,11 @@ import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration import org.springframework.boot.context.properties.ConfigurationPropertiesScan import org.springframework.boot.runApplication +import org.springframework.scheduling.annotation.EnableScheduling @SpringBootApplication(exclude=[DataSourceAutoConfiguration::class]) @ConfigurationPropertiesScan +@EnableScheduling class Application fun main(args: Array) { diff --git a/bpdm-cleaning-service-dummy/src/main/kotlin/org/eclipse/tractusx/bpdm/cleaning/config/ClientsConfig.kt b/bpdm-cleaning-service-dummy/src/main/kotlin/org/eclipse/tractusx/bpdm/cleaning/config/ClientsConfig.kt new file mode 100644 index 000000000..33c849960 --- /dev/null +++ b/bpdm-cleaning-service-dummy/src/main/kotlin/org/eclipse/tractusx/bpdm/cleaning/config/ClientsConfig.kt @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +package org.eclipse.tractusx.bpdm.cleaning.config + + +import org.eclipse.tractusx.orchestrator.api.client.OrchestrationApiClient +import org.eclipse.tractusx.orchestrator.api.client.OrchestrationApiClientImpl +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType +import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager +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.registration.ClientRegistrationRepository +import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction +import org.springframework.web.reactive.function.client.WebClient +import java.util.function.Consumer + + +@Configuration +class ClientsConfig { + + + @Bean + @ConditionalOnProperty( + value = ["bpdm.orchestrator.security-enabled"], + havingValue = "false", + matchIfMissing = true + ) + fun orchestratorClientNoAuth(poolConfigProperties: OrchestratorConfigProperties): OrchestrationApiClient { + val url = poolConfigProperties.baseUrl + return OrchestrationApiClientImpl { webClientBuilder(url).build() } + } + + + @Bean + @ConditionalOnProperty( + value = ["bpdm.orchestrator.security-enabled"], + havingValue = "true" + ) + fun orchestratorClientWithAuth( + poolConfigProperties: OrchestratorConfigProperties, + clientRegistrationRepository: ClientRegistrationRepository, + authorizedClientService: OAuth2AuthorizedClientService + ): OrchestrationApiClient { + val url = poolConfigProperties.baseUrl + val clientRegistrationId = poolConfigProperties.oauth2ClientRegistration + ?: throw IllegalArgumentException("bpdm.orchestrator.oauth2-client-registration is required if bpdm.orchestrator.security-enabled is set") + return OrchestrationApiClientImpl { + webClientBuilder(url) + .apply(oauth2Configuration(clientRegistrationRepository, authorizedClientService, clientRegistrationId)) + .build() + } + } + + + private fun webClientBuilder(url: String) = + WebClient.builder() + .baseUrl(url) + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + + private fun oauth2Configuration( + clientRegistrationRepository: ClientRegistrationRepository, + authorizedClientService: OAuth2AuthorizedClientService, + clientRegistrationId: String + ): Consumer { + val authorizedClientManager = authorizedClientManager(clientRegistrationRepository, authorizedClientService) + val oauth = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager) + oauth.setDefaultClientRegistrationId(clientRegistrationId) + return oauth.oauth2Configuration() + } + + private fun authorizedClientManager( + clientRegistrationRepository: ClientRegistrationRepository, + authorizedClientService: OAuth2AuthorizedClientService + ): OAuth2AuthorizedClientManager { + val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder().clientCredentials().build() + val authorizedClientManager = AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientService) + authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider) + return authorizedClientManager + } + +} \ No newline at end of file diff --git a/bpdm-cleaning-service-dummy/src/main/kotlin/org/eclipse/tractusx/bpdm/cleaning/config/OrchestratorConfigProperties.kt b/bpdm-cleaning-service-dummy/src/main/kotlin/org/eclipse/tractusx/bpdm/cleaning/config/OrchestratorConfigProperties.kt new file mode 100644 index 000000000..ba5bd6a53 --- /dev/null +++ b/bpdm-cleaning-service-dummy/src/main/kotlin/org/eclipse/tractusx/bpdm/cleaning/config/OrchestratorConfigProperties.kt @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +package org.eclipse.tractusx.bpdm.cleaning.config + +import org.springframework.boot.context.properties.ConfigurationProperties + + +@ConfigurationProperties(prefix = "bpdm.orchestrator") +data class OrchestratorConfigProperties( + val baseUrl: String = "http://localhost:8085/", + val securityEnabled: Boolean = false, + val oauth2ClientRegistration: String? +) diff --git a/bpdm-cleaning-service-dummy/src/main/kotlin/org/eclipse/tractusx/bpdm/cleaning/service/CleaningServiceDummy.kt b/bpdm-cleaning-service-dummy/src/main/kotlin/org/eclipse/tractusx/bpdm/cleaning/service/CleaningServiceDummy.kt new file mode 100644 index 000000000..0d367d59a --- /dev/null +++ b/bpdm-cleaning-service-dummy/src/main/kotlin/org/eclipse/tractusx/bpdm/cleaning/service/CleaningServiceDummy.kt @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +package org.eclipse.tractusx.bpdm.cleaning.service + + +import mu.KotlinLogging +import org.eclipse.tractusx.bpdm.common.dto.AddressType +import org.eclipse.tractusx.orchestrator.api.client.OrchestrationApiClient +import org.eclipse.tractusx.orchestrator.api.model.* +import org.springframework.scheduling.annotation.Scheduled +import org.springframework.stereotype.Service +import java.util.* + +@Service +class CleaningServiceDummy( + private val orchestrationApiClient: OrchestrationApiClient, + + ) { + + private val logger = KotlinLogging.logger { } + + + @Scheduled(cron = "\${cleaningService.pollingCron:-}", zone = "UTC") + fun pollForCleaningTasks() { + try { + logger.info { "Starting polling for cleaning tasks from Orchestrator..." } + + // Step 1: Fetch and reserve the next cleaning request + val cleaningRequest = orchestrationApiClient.goldenRecordTasks + .reserveTasksForStep(TaskStepReservationRequest(amount = 10, TaskStep.CleanAndSync)) + + val cleaningTasks = cleaningRequest.reservedTasks + + logger.info { "${cleaningTasks.size} tasks found for cleaning. Proceeding with cleaning..." } + + if (cleaningTasks.isNotEmpty()) { + + val cleaningResults = cleaningTasks.map { reservedTask -> + // Step 2: Generate dummy cleaning results + processCleaningTask(reservedTask) + } + + // Step 3: Send the cleaning result back to the Orchestrator + orchestrationApiClient.goldenRecordTasks.resolveStepResults(TaskStepResultRequest(cleaningResults)) + logger.info { "Cleaning tasks processing completed for this iteration." } + } + } catch (e: Exception) { + logger.error(e) { "Error while processing cleaning task" } + } + } + + fun processCleaningTask(reservedTask: TaskStepReservationEntryDto): TaskStepResultEntryDto { + val genericBusinessPartner = reservedTask.businessPartner.generic + + val addressPartner = createAddressRepresentation(genericBusinessPartner) + + val addressType = genericBusinessPartner.postalAddress.addressType ?: AddressType.AdditionalAddress + + val legalEntityDto = createLegalEntityRepresentation(addressPartner, addressType, genericBusinessPartner) + + val siteDto = createSiteDtoIfNeeded(genericBusinessPartner, addressPartner) + + val addressDto = shouldCreateAddress(addressType, addressPartner) + + return TaskStepResultEntryDto(reservedTask.taskId, BusinessPartnerFullDto(genericBusinessPartner, legalEntityDto, siteDto, addressDto)) + } + + private fun shouldCreateAddress( + addressType: AddressType, + addressPartner: LogisticAddressDto + ): LogisticAddressDto? { + val addressDto = if (addressType == AddressType.AdditionalAddress) { + addressPartner + } else { + null + } + return addressDto + } + + fun createSiteDtoIfNeeded(businessPartner: BusinessPartnerGenericDto, addressPartner: LogisticAddressDto): SiteDto? { + if (!shouldCreateSite(businessPartner)) return null + + val siteAddressReference = when (businessPartner.postalAddress.addressType) { + AddressType.SiteMainAddress, AddressType.LegalAndSiteMainAddress -> addressPartner.bpnAReference + else -> generateNewBpnRequestIdentifier() + } + + val siteMainAddress = addressPartner.copy(bpnAReference = siteAddressReference) + return createSiteRepresentation(businessPartner, siteMainAddress) + } + + fun createLegalEntityRepresentation( + addressPartner: LogisticAddressDto, + addressType: AddressType, + genericPartner: BusinessPartnerGenericDto + ): LegalEntityDto { + val legalAddressBpnReference = if (addressType == AddressType.LegalAddress || addressType == AddressType.LegalAndSiteMainAddress) { + addressPartner.bpnAReference + } else { + generateNewBpnRequestIdentifier() + } + + val legalAddress = addressPartner.copy(bpnAReference = legalAddressBpnReference) + + val bpnReferenceDto = createBpnReference(genericPartner.bpnL) + + return genericPartner.toLegalEntityDto(bpnReferenceDto, legalAddress) + + } + + fun createAddressRepresentation(genericPartner: BusinessPartnerGenericDto): LogisticAddressDto { + val bpnReferenceDto = createBpnReference(genericPartner.bpnA) + return genericPartner.toLogisticAddressDto(bpnReferenceDto) + } + + fun createSiteRepresentation(genericPartner: BusinessPartnerGenericDto, siteAddressReference: LogisticAddressDto): SiteDto { + val legalName = genericPartner.nameParts.joinToString(" ") + val bpnReferenceDto = createBpnReference(genericPartner.bpnS) + return genericPartner.toSiteDto(bpnReferenceDto, legalName, siteAddressReference) + } + + fun createBpnReference(bpn: String?): BpnReferenceDto { + return if (bpn != null) { + BpnReferenceDto(bpn, BpnReferenceType.Bpn) + } else { + // Generate a new UUID and create a BpnReferenceDto object if bpnL/bpnS/bpnA is null + generateNewBpnRequestIdentifier() + } + } + + private fun generateNewBpnRequestIdentifier() = BpnReferenceDto(UUID.randomUUID().toString(), BpnReferenceType.BpnRequestIdentifier) + + fun shouldCreateSite(genericPartner: BusinessPartnerGenericDto): Boolean { + return genericPartner.postalAddress.addressType == AddressType.SiteMainAddress || + genericPartner.postalAddress.addressType == AddressType.LegalAndSiteMainAddress || + genericPartner.bpnS != null + } + + +} diff --git a/bpdm-cleaning-service-dummy/src/main/kotlin/org/eclipse/tractusx/bpdm/cleaning/service/GenericBusinessPartnerMappings.kt b/bpdm-cleaning-service-dummy/src/main/kotlin/org/eclipse/tractusx/bpdm/cleaning/service/GenericBusinessPartnerMappings.kt new file mode 100644 index 000000000..1cc1fef71 --- /dev/null +++ b/bpdm-cleaning-service-dummy/src/main/kotlin/org/eclipse/tractusx/bpdm/cleaning/service/GenericBusinessPartnerMappings.kt @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +package org.eclipse.tractusx.bpdm.cleaning.service + + +import org.eclipse.tractusx.bpdm.common.dto.BusinessPartnerIdentifierDto +import org.eclipse.tractusx.bpdm.common.dto.BusinessPartnerStateDto +import org.eclipse.tractusx.bpdm.common.dto.ClassificationDto +import org.eclipse.tractusx.orchestrator.api.model.* + + +fun BusinessPartnerGenericDto.toLegalEntityDto(bpnReferenceDto: BpnReferenceDto, legalAddress: LogisticAddressDto): LegalEntityDto { + + return LegalEntityDto( + bpnLReference = bpnReferenceDto, + hasChanged = true, + legalName = nameParts.joinToString(" "), + legalShortName = shortName, + identifiers = identifiers.map { it.toLegalEntityIdentifierDto() }, + legalForm = legalForm, + states = states.map { it.toLegalEntityState() }, + classifications = classifications.map { it.toBusinessPartnerClassificationDto() }, + legalAddress = legalAddress + + ) +} + +fun ClassificationDto.toBusinessPartnerClassificationDto(): BusinessPartnerClassificationDto { + + return BusinessPartnerClassificationDto(code = code, type = type, value = value) +} + +fun BusinessPartnerIdentifierDto.toLegalEntityIdentifierDto(): LegalEntityIdentifierDto { + + return LegalEntityIdentifierDto(value = value, type = type, issuingBody = issuingBody) +} + +fun BusinessPartnerStateDto.toLegalEntityState(): LegalEntityState { + + return LegalEntityState(description, validFrom, validTo, type) +} + +fun BusinessPartnerStateDto.toSiteState(): SiteStateDto { + + return SiteStateDto(description, validFrom, validTo, type) +} + +fun BusinessPartnerGenericDto.toLogisticAddressDto(bpnReferenceDto: BpnReferenceDto): + LogisticAddressDto { + return LogisticAddressDto( + bpnAReference = bpnReferenceDto, + hasChanged = true, + name = nameParts.joinToString(" "), + states = emptyList(), + identifiers = emptyList(), + physicalPostalAddress = postalAddress.physicalPostalAddress, + alternativePostalAddress = postalAddress.alternativePostalAddress + ) +} + +fun BusinessPartnerGenericDto.toSiteDto(bpnReferenceDto: BpnReferenceDto, legalName: String, siteAddressReference: LogisticAddressDto): SiteDto { + + + return SiteDto( + bpnSReference = bpnReferenceDto, + hasChanged = true, + name = legalName, + states = states.map { it.toSiteState() }, + mainAddress = siteAddressReference + + ) +} + + + + + diff --git a/bpdm-cleaning-service-dummy/src/main/resources/application.properties b/bpdm-cleaning-service-dummy/src/main/resources/application.properties index 9a18363d8..74107e09c 100644 --- a/bpdm-cleaning-service-dummy/src/main/resources/application.properties +++ b/bpdm-cleaning-service-dummy/src/main/resources/application.properties @@ -39,4 +39,8 @@ springdoc.swagger-ui.csrf.enabled=true management.endpoint.health.probes.enabled=true management.health.livenessState.enabled=true management.health.readinessState.enabled=true - +#Connection to Orchestrator +bpdm.orchestrator.base-url=http://localhost:8085/ +## +#Cleaning Service Configurations +cleaningService.pollingCron=- diff --git a/bpdm-cleaning-service-dummy/src/test/kotlin/org/eclipse/tractusx/bpdm/cleaning/service/CleaningServiceApiCallsTest.kt b/bpdm-cleaning-service-dummy/src/test/kotlin/org/eclipse/tractusx/bpdm/cleaning/service/CleaningServiceApiCallsTest.kt new file mode 100644 index 000000000..3b72b28db --- /dev/null +++ b/bpdm-cleaning-service-dummy/src/test/kotlin/org/eclipse/tractusx/bpdm/cleaning/service/CleaningServiceApiCallsTest.kt @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +package org.eclipse.tractusx.bpdm.cleaning.service + + +import com.fasterxml.jackson.databind.ObjectMapper +import com.github.tomakehurst.wiremock.client.WireMock.* +import com.github.tomakehurst.wiremock.core.WireMockConfiguration +import com.github.tomakehurst.wiremock.junit5.WireMockExtension +import org.eclipse.tractusx.bpdm.cleaning.testdata.CommonValues.businessPartnerWithBpnA +import org.eclipse.tractusx.bpdm.cleaning.testdata.CommonValues.fixedTaskId + +import org.eclipse.tractusx.bpdm.common.dto.* +import org.eclipse.tractusx.orchestrator.api.client.OrchestrationApiClient +import org.eclipse.tractusx.orchestrator.api.model.* +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.context.DynamicPropertyRegistry +import org.springframework.test.context.DynamicPropertySource +import java.time.Instant +import java.util.* + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ActiveProfiles("test") +class CleaningServiceApiCallsTest @Autowired constructor( + val cleaningServiceDummy: CleaningServiceDummy, + val jacksonObjectMapper: ObjectMapper, + val orchestrationApiClient: OrchestrationApiClient +) { + + companion object { + const val ORCHESTRATOR_RESERVE_TASKS_URL = "/api/golden-record-tasks/step-reservations" + const val ORCHESTRATOR_RESOLVE_TASKS_URL = "/api/golden-record-tasks/step-results" + + @JvmField + @RegisterExtension + val orchestratorMockApi: WireMockExtension = WireMockExtension.newInstance() + .options(WireMockConfiguration.wireMockConfig().dynamicPort()) + .build() + + @JvmStatic + @DynamicPropertySource + fun properties(registry: DynamicPropertyRegistry) { + registry.add("bpdm.orchestrator.base-url") { orchestratorMockApi.baseUrl() } + + } + } + + @BeforeEach + fun beforeEach() { + orchestratorMockApi.resetAll() + this.mockOrchestratorResolveApi() + this.mockOrchestratorReserveApi() + } + + + @Test + fun `pollForCleaningTasks should reserve and resolve tasks from orchestrator`() { + + // Call the method under test + cleaningServiceDummy.pollForCleaningTasks() + + // Verify that the reserve API was called once (using WireMock) + orchestratorMockApi.verify(postRequestedFor(urlEqualTo(ORCHESTRATOR_RESERVE_TASKS_URL)).withRequestBody(matchingJsonPath("$.amount", equalTo("10")))) + + // Verify that the resolve API was called once (using WireMock) + orchestratorMockApi.verify(postRequestedFor(urlEqualTo(ORCHESTRATOR_RESOLVE_TASKS_URL))) + } + + @Test + fun `reserveTasksForStep should return expected response`() { + + + val expectedResponse = jacksonObjectMapper.writeValueAsString(createSampleTaskStepReservationResponse(businessPartnerWithBpnA)) + + + val result = orchestrationApiClient.goldenRecordTasks.reserveTasksForStep( + TaskStepReservationRequest(amount = 10, TaskStep.Clean) + ) + + // Assert the expected result + val expectedResult = jacksonObjectMapper.readValue(expectedResponse, result::class.java) // Convert the expected JSON response to your DTO + assertEquals(expectedResult, result) + + orchestrationApiClient.goldenRecordTasks.resolveStepResults( + TaskStepResultRequest(emptyList()) + ) + + } + + + fun mockOrchestratorReserveApi() { + + // Orchestrator reserve + orchestratorMockApi.stubFor( + post(urlPathEqualTo(ORCHESTRATOR_RESERVE_TASKS_URL)) + .willReturn( + okJson(jacksonObjectMapper.writeValueAsString(createSampleTaskStepReservationResponse(businessPartnerWithBpnA))) + ) + ) + } + + fun mockOrchestratorResolveApi() { + // Orchestrator resolve + orchestratorMockApi.stubFor( + post(urlPathEqualTo(ORCHESTRATOR_RESOLVE_TASKS_URL)) + .willReturn(aResponse().withStatus(200)) + ) + } + + // Helper method to create a sample TaskStepReservationResponse + private fun createSampleTaskStepReservationResponse(businessPartnerGenericDto: BusinessPartnerGenericDto): TaskStepReservationResponse { + val fullDto = BusinessPartnerFullDto(businessPartnerGenericDto) + return TaskStepReservationResponse(listOf(TaskStepReservationEntryDto(fixedTaskId, fullDto)), Instant.MIN) + } + +} \ No newline at end of file diff --git a/bpdm-cleaning-service-dummy/src/test/kotlin/org/eclipse/tractusx/bpdm/cleaning/service/CleaningServiceDummyTest.kt b/bpdm-cleaning-service-dummy/src/test/kotlin/org/eclipse/tractusx/bpdm/cleaning/service/CleaningServiceDummyTest.kt new file mode 100644 index 000000000..8769212aa --- /dev/null +++ b/bpdm-cleaning-service-dummy/src/test/kotlin/org/eclipse/tractusx/bpdm/cleaning/service/CleaningServiceDummyTest.kt @@ -0,0 +1,254 @@ +/******************************************************************************* + * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +package org.eclipse.tractusx.bpdm.cleaning.service + +import com.github.tomakehurst.wiremock.client.WireMock.* +import org.assertj.core.api.Assertions +import org.assertj.core.api.RecursiveComparisonAssert +import org.eclipse.tractusx.bpdm.cleaning.testdata.CommonValues.businessPartnerWithBpnA +import org.eclipse.tractusx.bpdm.cleaning.testdata.CommonValues.businessPartnerWithBpnLAndBpnAAndLegalAddressType +import org.eclipse.tractusx.bpdm.cleaning.testdata.CommonValues.businessPartnerWithBpnSAndBpnAAndLegalAndSiteMainAddressType +import org.eclipse.tractusx.bpdm.cleaning.testdata.CommonValues.businessPartnerWithEmptyBpnAndSiteMainAddressType +import org.eclipse.tractusx.bpdm.cleaning.testdata.CommonValues.businessPartnerWithEmptyBpnLAndAdditionalAddressType +import org.eclipse.tractusx.bpdm.cleaning.testdata.CommonValues.expectedLegalEntityDto +import org.eclipse.tractusx.bpdm.cleaning.testdata.CommonValues.expectedLogisticAddressDto +import org.eclipse.tractusx.bpdm.cleaning.testdata.CommonValues.expectedSiteDto +import org.eclipse.tractusx.bpdm.common.dto.* +import org.eclipse.tractusx.orchestrator.api.model.* +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ActiveProfiles +import java.time.Instant +import java.util.* + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ActiveProfiles("test") +class CleaningServiceDummyTest @Autowired constructor( + val cleaningServiceDummy: CleaningServiceDummy +) { + + + @Test + fun `test processCleaningTask with BpnA present and additional address type`() { + val taskStepReservationEntryDto = createSampleTaskStepReservationResponse(businessPartnerWithBpnA).reservedTasks[0] + + val result = cleaningServiceDummy.processCleaningTask(taskStepReservationEntryDto) + + val expectedBpnA = taskStepReservationEntryDto.businessPartner.generic.bpnA + + val resultedAddress = result.businessPartner?.address + + val resultedLegalEntity = result.businessPartner?.legalEntity + + // legalEntity should be Generated with new bpnL and legalAddress bpnA + // addressPartner should use passed bpnA, and it will be different from legalAddress since type is additional address type + + assertEquals(expectedBpnA, resultedAddress?.bpnAReference?.referenceValue) + + assertEquals(BpnReferenceType.BpnRequestIdentifier, resultedLegalEntity?.bpnLReference?.referenceType) + + val expectedAddress = expectedLogisticAddressDto.copy(bpnAReference = BpnReferenceDto(expectedBpnA.toString(), BpnReferenceType.Bpn)) + + val expectedLegalEntity = expectedLegalEntityDto.copy(legalAddress = expectedLogisticAddressDto) + + assertRecursively(resultedAddress).isEqualTo(expectedAddress) + + // ignoring bpnLReference and bpnAReference since they are generated + assertRecursively(resultedLegalEntity).ignoringFields("bpnLReference", "legalAddress.bpnAReference").isEqualTo(expectedLegalEntity) + + + } + + @Test + fun `test processCleaningTask with empty BpnA and additional address type`() { + val taskStepReservationEntryDto = createSampleTaskStepReservationResponse(businessPartnerWithEmptyBpnLAndAdditionalAddressType).reservedTasks[0] + + val result = cleaningServiceDummy.processCleaningTask(taskStepReservationEntryDto) + + val resultedAddress = result.businessPartner?.address + + val resultedLegalEntity = result.businessPartner?.legalEntity + + // legalEntity should be Generated with new bpnL and legalAddress bpnA + // addressPartner should be Generated bpnA, and it will be different from legalAddress since type is additional address type + + assertEquals(BpnReferenceType.BpnRequestIdentifier, resultedAddress?.bpnAReference?.referenceType) + + assertEquals(BpnReferenceType.BpnRequestIdentifier, resultedLegalEntity?.bpnLReference?.referenceType) + + assertNotEquals(resultedAddress?.bpnAReference?.referenceValue, resultedLegalEntity?.legalAddress?.bpnAReference?.referenceValue) + + val expectedAddress = expectedLogisticAddressDto.copy() + + val expectedLegalEntity = expectedLegalEntityDto.copy(legalAddress = expectedLogisticAddressDto) + + // bpnAReference since they are generated + assertRecursively(resultedAddress).ignoringFields("bpnAReference").isEqualTo(expectedAddress) + + // ignoring bpnLReference and bpnAReference since they are generated + assertRecursively(resultedLegalEntity).ignoringFields("bpnAReference", "bpnLReference", "legalAddress.bpnAReference").isEqualTo(expectedLegalEntity) + } + + @Test + fun `test processCleaningTask with BpnL and BpnA present and legal address type`() { + val taskStepReservationEntryDto = createSampleTaskStepReservationResponse(businessPartnerWithBpnLAndBpnAAndLegalAddressType).reservedTasks[0] + + val result = cleaningServiceDummy.processCleaningTask(taskStepReservationEntryDto) + + val expectedBpnA = taskStepReservationEntryDto.businessPartner.generic.bpnA + + val expectedBpnL = taskStepReservationEntryDto.businessPartner.generic.bpnL + + val resultedAddress = result.businessPartner?.address + + val resultedLegalEntity = result.businessPartner?.legalEntity + + // legalEntity should use passed bpnL and legalAddress should use passed bpnA since address type is LegalAddressType + // addressPartner should use null, since it does not create when type is LegalAddressType + + assertNull(resultedAddress?.bpnAReference?.referenceValue) + assertEquals(expectedBpnL, resultedLegalEntity?.bpnLReference?.referenceValue) + assertEquals(expectedBpnA, resultedLegalEntity?.legalAddress?.bpnAReference?.referenceValue) + + + val expectedLegalEntity = expectedLegalEntityDto.copy( + legalAddress = expectedLogisticAddressDto.copy( + bpnAReference = BpnReferenceDto( + expectedBpnA.toString(), + BpnReferenceType.Bpn + ) + ), bpnLReference = BpnReferenceDto(expectedBpnL.toString(), BpnReferenceType.Bpn) + ) + + assertRecursively(resultedLegalEntity).isEqualTo(expectedLegalEntity) + } + + @Test + fun `test processCleaningTask with BpnS and BpnA present and legal and site main address type`() { + val taskStepReservationResponse = createSampleTaskStepReservationResponse(businessPartnerWithBpnSAndBpnAAndLegalAndSiteMainAddressType).reservedTasks[0] + + val result = cleaningServiceDummy.processCleaningTask(taskStepReservationResponse) + + val resultedAddress = result.businessPartner?.address + + val resultedLegalEntity = result.businessPartner?.legalEntity + + val resultedSite = result.businessPartner?.site + + val expectedBpnA = taskStepReservationResponse.businessPartner.generic.bpnA + + val expectedBpnS = taskStepReservationResponse.businessPartner.generic.bpnS + + + // legalEntity should Generate new bpnL and legalAddress should use passed bpnA since address type is LegalAndSiteMainAddress + // addressPartner should use null, since it does not create when type is LegalAndSiteMainAddress + // Site should use passed bpnS, and it will be the same MainAddress as legalAddress and addressPartner since address type is LegalAndSiteMainAddress + + + assertNull(resultedAddress?.bpnAReference?.referenceValue) + + assertEquals(expectedBpnA, resultedLegalEntity?.legalAddress?.bpnAReference?.referenceValue) + + assertEquals(expectedBpnA, resultedSite?.mainAddress?.bpnAReference?.referenceValue) + + assertEquals(BpnReferenceType.BpnRequestIdentifier, resultedLegalEntity?.bpnLReference?.referenceType) + + assertEquals(expectedBpnS, resultedSite?.bpnSReference?.referenceValue) + + + val expectedLegalEntity = expectedLegalEntityDto.copy( + legalAddress = expectedLogisticAddressDto.copy( + bpnAReference = BpnReferenceDto( + expectedBpnA.toString(), + BpnReferenceType.Bpn + ) + ) + ) + + val expectedSite = expectedSiteDto.copy( + mainAddress = expectedLogisticAddressDto.copy(bpnAReference = BpnReferenceDto(expectedBpnA.toString(), BpnReferenceType.Bpn)), + bpnSReference = BpnReferenceDto(expectedBpnS.toString(), BpnReferenceType.Bpn) + ) + + + // ignoring bpnLReference since they are generated + assertRecursively(resultedLegalEntity).ignoringFields("bpnLReference").isEqualTo(expectedLegalEntity) + + assertRecursively(resultedSite).isEqualTo(expectedSite) + + + } + @Test + fun `test processCleaningTask with empty Bpn and site main address type`() { + val taskStepReservationResponse = createSampleTaskStepReservationResponse(businessPartnerWithEmptyBpnAndSiteMainAddressType).reservedTasks[0] + + val result = cleaningServiceDummy.processCleaningTask(taskStepReservationResponse) + + val resultedAddress = result.businessPartner?.address + + val resultedLegalEntity = result.businessPartner?.legalEntity + + val resultedSite = result.businessPartner?.site + + + // legalEntity should Generate new bpnL and legalAddress should Generate new bpnA since address type is SiteMainAddress + // addressPartner should use null, since it does not create when type is SiteMainAddress + // Site should Generate new bpnS + + + assertEquals(BpnReferenceType.BpnRequestIdentifier, resultedLegalEntity?.bpnLReference?.referenceType) + + assertNull(resultedAddress?.bpnAReference?.referenceType) + + assertEquals(BpnReferenceType.BpnRequestIdentifier, resultedSite?.bpnSReference?.referenceType) + + assertNotEquals(resultedAddress?.bpnAReference?.referenceValue, resultedLegalEntity?.legalAddress?.bpnAReference?.referenceValue) + assertNotEquals(resultedAddress?.bpnAReference?.referenceValue, resultedSite?.mainAddress?.bpnAReference?.referenceValue) + + val expectedLegalEntity = expectedLegalEntityDto.copy(legalAddress = expectedLogisticAddressDto.copy()) + + val expectedSite = expectedSiteDto.copy(mainAddress = expectedLogisticAddressDto.copy()) + + + // ignoring bpnLReference and legalAddress.bpnAReference since they are generated + assertRecursively(resultedLegalEntity).ignoringFields("bpnLReference", "legalAddress.bpnAReference").isEqualTo(expectedLegalEntity) + + // ignoring bpnSReference and mainAddress.bpnAReference since they are generated + assertRecursively(resultedSite).ignoringFields("bpnSReference", "mainAddress.bpnAReference").isEqualTo(expectedSite) + + } + + // Helper method to create a sample TaskStepReservationResponse + private fun createSampleTaskStepReservationResponse(businessPartnerGenericDto: BusinessPartnerGenericDto): TaskStepReservationResponse { + val fullDto = BusinessPartnerFullDto(businessPartnerGenericDto) + return TaskStepReservationResponse(listOf(TaskStepReservationEntryDto(UUID.randomUUID().toString(), fullDto)), Instant.MIN) + } + + fun assertRecursively(actual: T): RecursiveComparisonAssert<*> { + return Assertions.assertThat(actual) + .usingRecursiveComparison() + .ignoringCollectionOrder() + .ignoringAllOverriddenEquals() + .ignoringFieldsOfTypes(Instant::class.java) + } + +} diff --git a/bpdm-cleaning-service-dummy/src/test/kotlin/org/eclipse/tractusx/bpdm/cleaning/testdata/CommonValues.kt b/bpdm-cleaning-service-dummy/src/test/kotlin/org/eclipse/tractusx/bpdm/cleaning/testdata/CommonValues.kt new file mode 100644 index 000000000..3cb5b8f1c --- /dev/null +++ b/bpdm-cleaning-service-dummy/src/test/kotlin/org/eclipse/tractusx/bpdm/cleaning/testdata/CommonValues.kt @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ******************************************************************************/ + +package org.eclipse.tractusx.bpdm.cleaning.testdata + +import com.neovisionaries.i18n.CountryCode +import org.eclipse.tractusx.bpdm.cleaning.service.toBusinessPartnerClassificationDto +import org.eclipse.tractusx.bpdm.cleaning.service.toLegalEntityIdentifierDto +import org.eclipse.tractusx.bpdm.cleaning.service.toLegalEntityState +import org.eclipse.tractusx.bpdm.cleaning.service.toSiteState +import org.eclipse.tractusx.bpdm.common.dto.* +import org.eclipse.tractusx.bpdm.common.model.BusinessStateType +import org.eclipse.tractusx.bpdm.common.model.ClassificationType +import org.eclipse.tractusx.orchestrator.api.model.* +import org.eclipse.tractusx.orchestrator.api.model.LegalEntityDto +import org.eclipse.tractusx.orchestrator.api.model.LogisticAddressDto +import org.eclipse.tractusx.orchestrator.api.model.PhysicalPostalAddressDto +import org.eclipse.tractusx.orchestrator.api.model.SiteDto +import org.eclipse.tractusx.orchestrator.api.model.StreetDto +import java.time.LocalDateTime + +/** + * Contains simple test values used to create more complex test values such as DTOs + */ +object CommonValues { + + val fixedTaskId = "taskid-123123" + + val nameParts = listOf("Part1", "Part2") + const val shortName = "ShortName" + val identifiers = listOf( + BusinessPartnerIdentifierDto( + type = "Type1", + value = "Value1", + issuingBody = "IssuingBody1" + ) + ) + const val legalForm = "LegalForm" + val states = listOf( + BusinessPartnerStateDto( + validFrom = LocalDateTime.now(), + validTo = LocalDateTime.now().plusDays(10), + type = BusinessStateType.ACTIVE, + description = "ActiveState" + ) + ) + val classifications = listOf( + ClassificationDto( + type = ClassificationType.NACE, + code = "Code1", + value = "Value1" + ) + ) + val roles = listOf(BusinessPartnerRole.SUPPLIER, BusinessPartnerRole.CUSTOMER) + val physicalPostalAddress = PhysicalPostalAddressDto( + geographicCoordinates = GeoCoordinateDto(longitude = 12.34f, latitude = 56.78f), + country = CountryCode.PT, + administrativeAreaLevel1 = "AdminArea1", + administrativeAreaLevel2 = "AdminArea2", + administrativeAreaLevel3 = "AdminArea3", + postalCode = "PostalCode", + city = "City", + district = "District", + street = StreetDto("StreetName"), + companyPostalCode = "CompanyPostalCode", + industrialZone = "IndustrialZone", + building = "Building", + floor = "Floor", + door = "Door" + ) + + val postalAddressForLegalAndSite = PostalAddressDto( + addressType = AddressType.LegalAndSiteMainAddress, + physicalPostalAddress = physicalPostalAddress + ) + val postalAddressForLegal = PostalAddressDto( + addressType = AddressType.LegalAddress, + physicalPostalAddress = physicalPostalAddress + ) + val postalAddressForSite = PostalAddressDto( + addressType = AddressType.SiteMainAddress, + physicalPostalAddress = physicalPostalAddress + ) + val postalAddressForAdditional = PostalAddressDto( + addressType = AddressType.AdditionalAddress, + physicalPostalAddress = physicalPostalAddress + ) + + val businessPartnerWithEmptyBpns = BusinessPartnerGenericDto( + nameParts = nameParts, + shortName = shortName, + identifiers = identifiers, + legalForm = legalForm, + states = states, + classifications = classifications, + roles = roles, + ownerBpnL = "ownerBpnL2" + ) + + + val businessPartnerWithBpnA = businessPartnerWithEmptyBpns.copy( + postalAddress = postalAddressForAdditional, + bpnA = "FixedBPNA" + ) + + + val businessPartnerWithBpnLAndBpnAAndLegalAddressType = businessPartnerWithEmptyBpns.copy( + postalAddress = postalAddressForLegal, + bpnA = "FixedBPNA", + bpnL = "FixedBPNL" + ) + + val businessPartnerWithEmptyBpnLAndAdditionalAddressType = businessPartnerWithEmptyBpns.copy( + postalAddress = postalAddressForAdditional, + ) + + val businessPartnerWithBpnSAndBpnAAndLegalAndSiteMainAddressType = businessPartnerWithEmptyBpns.copy( + postalAddress = postalAddressForLegalAndSite, + bpnA = "FixedBPNA", + bpnS = "FixedBPNS" + ) + + val businessPartnerWithEmptyBpnAndSiteMainAddressType = businessPartnerWithEmptyBpns.copy( + postalAddress = postalAddressForSite + ) + + val expectedLegalEntityDto = LegalEntityDto( + hasChanged = true, + legalName = nameParts.joinToString(" "), + legalShortName = shortName, + identifiers = identifiers.map { it.toLegalEntityIdentifierDto() }, + legalForm = legalForm, + states = states.map { it.toLegalEntityState() }, + classifications = classifications.map { it.toBusinessPartnerClassificationDto() } + ) + + val expectedSiteDto = SiteDto( + hasChanged = true, + name = nameParts.joinToString(" "), + states = states.map { it.toSiteState() }, + ) + + val expectedLogisticAddressDto = LogisticAddressDto( + + hasChanged = true, + name = nameParts.joinToString(" "), + states = emptyList(), + identifiers = emptyList(), + physicalPostalAddress = physicalPostalAddress + ) + + +} \ No newline at end of file