diff --git a/bpdm-common/src/main/kotlin/org/eclipse/tractusx/bpdm/common/dto/SiteStateDto.kt b/bpdm-common/src/main/kotlin/org/eclipse/tractusx/bpdm/common/dto/SiteStateDto.kt index f308549ae..3be145ddd 100644 --- a/bpdm-common/src/main/kotlin/org/eclipse/tractusx/bpdm/common/dto/SiteStateDto.kt +++ b/bpdm-common/src/main/kotlin/org/eclipse/tractusx/bpdm/common/dto/SiteStateDto.kt @@ -27,15 +27,11 @@ import java.time.LocalDateTime @Schema(description = SiteStateDescription.header) data class SiteStateDto( - @get:Schema(description = SiteStateDescription.description) - val description: String?, + override val description: String?, - @get:Schema(description = SiteStateDescription.validFrom) - val validFrom: LocalDateTime?, + override val validFrom: LocalDateTime?, - @get:Schema(description = SiteStateDescription.validTo) - val validTo: LocalDateTime?, + override val validTo: LocalDateTime?, - @get:Schema(description = SiteStateDescription.type) - val type: BusinessStateType -) \ No newline at end of file + override val type: BusinessStateType +) : IBaseSiteStateDto \ No newline at end of file diff --git a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/component/opensearch/impl/service/OpenSearchSyncStarterService.kt b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/component/opensearch/impl/service/OpenSearchSyncStarterService.kt index f641c6796..c1819d6b5 100644 --- a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/component/opensearch/impl/service/OpenSearchSyncStarterService.kt +++ b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/component/opensearch/impl/service/OpenSearchSyncStarterService.kt @@ -122,7 +122,7 @@ class OpenSearchSyncStarterService( * @return true if index mapping changed, false otherwise */ private fun updateOnInit(indexDefinition: IndexDefinition): Boolean { - val indexAlreadyExists = openSearchClient.indices().exists { it.index(indexDefinition.indexName) }.value() +/* val indexAlreadyExists = openSearchClient.indices().exists { it.index(indexDefinition.indexName) }.value() return if (!indexAlreadyExists) { true @@ -137,7 +137,8 @@ class OpenSearchSyncStarterService( deleteIndexIfExists(tempIndexName) requiredMappingMetadata != existingMappingMetadata - } + }*/ + return true } private fun getIndexMappings(indexName: String): MappingMetadata { diff --git a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/config/CleaningServiceClientsConfig.kt b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/config/CleaningServiceClientsConfig.kt new file mode 100644 index 000000000..e425f9cfe --- /dev/null +++ b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/config/CleaningServiceClientsConfig.kt @@ -0,0 +1,53 @@ +/******************************************************************************* + * 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.pool.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.web.reactive.function.client.WebClient + + +@Configuration +class CleaningServiceClientsConfig { + + // Pool-Client without authentication + @Bean + @ConditionalOnProperty( + value = ["bpdm.pool.security-enabled"], + havingValue = "false", + matchIfMissing = true + ) + fun poolClientNoAuth(configProperties: CleaningServiceConfigProperties): OrchestrationApiClient { + val url = configProperties.baseUrl + return OrchestrationApiClientImpl { webClientBuilder(url).build() } + } + + + private fun webClientBuilder(url: String) = + WebClient.builder() + .baseUrl(url) + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + +} \ No newline at end of file diff --git a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/config/CleaningServiceConfigProperties.kt b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/config/CleaningServiceConfigProperties.kt new file mode 100644 index 000000000..49f5e59a8 --- /dev/null +++ b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/config/CleaningServiceConfigProperties.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.pool.config + +import org.springframework.boot.context.properties.ConfigurationProperties + + +@ConfigurationProperties(prefix = "bpdm.pool") +data class CleaningServiceConfigProperties( + val baseUrl: String = "http://localhost:8080/", + val securityEnabled: Boolean = false, + val oauth2ClientRegistration: String? +) diff --git a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/exception/BpdmValidationException.kt b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/exception/BpdmValidationException.kt new file mode 100644 index 000000000..a2075d62d --- /dev/null +++ b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/exception/BpdmValidationException.kt @@ -0,0 +1,23 @@ +/******************************************************************************* + * 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.pool.exception + + +class BpdmValidationException(message: String) : RuntimeException(message) \ No newline at end of file diff --git a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/BusinessPartnerBuildService.kt b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/BusinessPartnerBuildService.kt index 811514217..6b294c3a6 100644 --- a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/BusinessPartnerBuildService.kt +++ b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/BusinessPartnerBuildService.kt @@ -63,7 +63,8 @@ class BusinessPartnerBuildService( val errorsByRequest = requestValidationService.validateLegalEntityCreates(requests.associateWith { it.legalEntity }) { theRequest -> theRequest.index } - val errorsByRequestAddress = requestValidationService.validateLegalEntityCreatesAddresses(requests.associateWith { it.legalAddress }) { theRequest -> theRequest.index } + val errorsByRequestAddress = + requestValidationService.validateLegalEntityCreatesAddresses(requests.associateWith { it.legalAddress }) { theRequest -> theRequest.index } val errors = errorsByRequest.flatMap { it.value } + errorsByRequestAddress.flatMap { it.value } val validRequests = requests.filterNot { errorsByRequest.containsKey(it) || errorsByRequestAddress.containsKey(it) } @@ -350,7 +351,7 @@ class BusinessPartnerBuildService( legalEntity = partner, ) - site.states.addAll(request.states.map { toEntity(it, site) }) + site.states.addAll(request.states.map { toSiteState(it, site) }) return site } @@ -376,16 +377,16 @@ class BusinessPartnerBuildService( partner.states.clear() partner.classifications.clear() - partner.states.addAll(request.states.map { toEntity(it, partner) }) - partner.identifiers.addAll(request.identifiers.map { toEntity(it, metadataMap, partner) }) - partner.classifications.addAll(request.classifications.map { toEntity(it, partner) }.toSet()) + partner.states.addAll(request.states.map { toLegalEntityState(it, partner) }) + partner.identifiers.addAll(request.identifiers.map { toLegalEntityIdentifier(it, metadataMap.idTypes, partner) }) + partner.classifications.addAll(request.classifications.map { toLegalEntityClassification(it, partner) }.toSet()) } private fun updateSite(site: Site, request: SiteDto) { site.name = request.name site.states.clear() - site.states.addAll(request.states.map { toEntity(it, site) }) + site.states.addAll(request.states.map { toSiteState(it, site) }) } private fun createLogisticAddress( @@ -430,11 +431,11 @@ class BusinessPartnerBuildService( address.identifiers.apply { clear() - addAll(dto.identifiers.map { toEntity(it, metadataMap.idTypes, address) }) + addAll(dto.identifiers.map { toAddressIdentifier(it, metadataMap.idTypes, address) }) } address.states.apply { clear() - addAll(dto.states.map { toEntity(it, address) }) + addAll(dto.states.map { toAddressState(it, address) }) } } @@ -457,7 +458,7 @@ class BusinessPartnerBuildService( ) } - private fun createAlternativeAddress(alternativeAddress: AlternativePostalAddressDto, regions: Map): AlternativePostalAddress { + private fun createAlternativeAddress(alternativeAddress: AlternativePostalAddressDto, regions: Map): AlternativePostalAddress { return AlternativePostalAddress( geographicCoordinates = alternativeAddress.geographicCoordinates?.let { toEntity(it) }, country = alternativeAddress.country, @@ -470,86 +471,10 @@ class BusinessPartnerBuildService( ) } - private fun createStreet(dto: StreetDto): Street { - return Street( - name = dto.name, - houseNumber = dto.houseNumber, - milestone = dto.milestone, - direction = dto.direction - ) - } - - private fun toEntity(dto: LegalEntityStateDto, legalEntity: LegalEntity): LegalEntityState { - return LegalEntityState( - description = dto.description, - validFrom = dto.validFrom, - validTo = dto.validTo, - type = dto.type, - legalEntity = legalEntity - ) - } - - private fun toEntity(dto: SiteStateDto, site: Site): SiteState { - return SiteState( - description = dto.description, - validFrom = dto.validFrom, - validTo = dto.validTo, - type = dto.type, - site = site - ) - } - - private fun toEntity(dto: AddressStateDto, address: LogisticAddress): AddressState { - return AddressState( - description = dto.description, - validFrom = dto.validFrom, - validTo = dto.validTo, - type = dto.type, - address = address - ) - } - - private fun toEntity(dto: ClassificationDto, partner: LegalEntity): LegalEntityClassification { - return LegalEntityClassification( - value = dto.value, - code = dto.code, - type = dto.type, - legalEntity = partner - ) - } - - private fun toEntity( - dto: LegalEntityIdentifierDto, - metadataMap: LegalEntityMetadataMapping, - partner: LegalEntity - ): LegalEntityIdentifier { - return LegalEntityIdentifier( - value = dto.value, - type = metadataMap.idTypes[dto.type]!!, - issuingBody = dto.issuingBody, - legalEntity = partner - ) - } - - private fun toEntity( - dto: AddressIdentifierDto, - idTypes: Map, - partner: LogisticAddress - ): AddressIdentifier { - return AddressIdentifier( - value = dto.value, - type = idTypes[dto.type]!!, - address = partner - ) - } - - private fun toEntity(dto: GeoCoordinateDto): GeographicCoordinate { + fun toEntity(dto: GeoCoordinateDto): GeographicCoordinate { return GeographicCoordinate(dto.latitude, dto.longitude, dto.altitude) } - private fun createCurrentnessTimestamp(): Instant { - return Instant.now().truncatedTo(ChronoUnit.MICROS) - } private fun LegalEntityMetadataDto.toMapping() = LegalEntityMetadataMapping( @@ -574,4 +499,85 @@ class BusinessPartnerBuildService( val regions: Map ) + companion object { + + fun createCurrentnessTimestamp(): Instant { + return Instant.now().truncatedTo(ChronoUnit.MICROS) + } + + fun createStreet(dto: IBaseStreetDto): Street { + return Street( + name = dto.name, + houseNumber = dto.houseNumber, + milestone = dto.milestone, + direction = dto.direction + ) + } + + fun toLegalEntityState(dto: IBaseLegalEntityStateDto, legalEntity: LegalEntity): LegalEntityState { + return LegalEntityState( + description = dto.description, + validFrom = dto.validFrom, + validTo = dto.validTo, + type = dto.type, + legalEntity = legalEntity + ) + } + + fun toSiteState(dto: IBaseSiteStateDto, site: Site): SiteState { + return SiteState( + description = dto.description, + validFrom = dto.validFrom, + validTo = dto.validTo, + type = dto.type, + site = site + ) + } + + fun toAddressState(dto: IBaseAddressStateDto, address: LogisticAddress): AddressState { + return AddressState( + description = dto.description, + validFrom = dto.validFrom, + validTo = dto.validTo, + type = dto.type, + address = address + ) + } + + fun toLegalEntityClassification(dto: IBaseClassificationDto, partner: LegalEntity): LegalEntityClassification { + return LegalEntityClassification( + value = dto.value, + code = dto.code, + type = dto.type, + legalEntity = partner + ) + } + + fun toLegalEntityIdentifier( + dto: IBaseLegalEntityIdentifierDto, + idTypes: Map, + partner: LegalEntity + ): LegalEntityIdentifier { + return LegalEntityIdentifier( + value = dto.value, + type = idTypes[dto.type]!!, + issuingBody = dto.issuingBody, + legalEntity = partner + ) + } + + fun toAddressIdentifier( + dto: IBaseAddressIdentifierDto, + idTypes: Map, + partner: LogisticAddress + ): AddressIdentifier { + return AddressIdentifier( + value = dto.value, + type = idTypes[dto.type]!!, + address = partner + ) + } + + } + } \ No newline at end of file diff --git a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/FetchAndReserveCleaningStepService.kt b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/FetchAndReserveCleaningStepService.kt deleted file mode 100644 index cb0872273..000000000 --- a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/FetchAndReserveCleaningStepService.kt +++ /dev/null @@ -1,198 +0,0 @@ -/******************************************************************************* - * 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.pool.service - -import mu.KotlinLogging -import org.eclipse.tractusx.bpdm.common.dto.IBaseLegalEntityDto -import org.eclipse.tractusx.bpdm.common.dto.IBaseLogisticAddressDto -import org.eclipse.tractusx.bpdm.pool.api.model.response.ErrorInfo -import org.eclipse.tractusx.bpdm.pool.api.model.response.LegalEntityCreateError -import org.eclipse.tractusx.bpdm.pool.dto.LegalEntityMetadataDto -import org.eclipse.tractusx.bpdm.pool.entity.* -import org.eclipse.tractusx.bpdm.pool.entity.LegalEntityState -import org.eclipse.tractusx.orchestrator.api.client.OrchestrationApiClient -import org.eclipse.tractusx.orchestrator.api.model.* -import org.springframework.scheduling.annotation.Scheduled -import java.time.Instant -import java.time.temporal.ChronoUnit - -class FetchAndReserveCleaningStepService( - private val orchestrationClient: OrchestrationApiClient, - private val metadataService: MetadataService, - private val requestValidationService: RequestValidationService, - private val bpnIssuingService: BpnIssuingService, -) { - private val logger = KotlinLogging.logger { } - - @Scheduled(cron = "\${bpdm.opensearch.export-scheduler-cron-expr:-}", zone = "UTC") - fun fetchAndReserve() { - - val reservationRequest = TaskStepReservationRequest(step = TaskStep.PoolSync, amount = 10) - val taskStepReservation = orchestrationClient.goldenRecordTasks.reserveTasksForStep(reservationRequest = reservationRequest) - - val taksResults = upsertGoldenRecordIntoPool(taskStepReservation.reservedTasks) - orchestrationClient.goldenRecordTasks.resolveStepResults(TaskStepResultRequest(taksResults)) - } - - private fun upsertGoldenRecordIntoPool(tasks: List): List { - - val isTaskCreateLegalEntity = { task: TaskStepReservationEntryDto -> task.businessPartner.legalEntity?.bpnLReference?.referenceType == BpnReferenceType.BpnRequestIdentifier } - - val legalEntitiesToCreateSteps : List = tasks - .filter { isTaskCreateLegalEntity(it) } - - val legalEntityByTask = legalEntitiesToCreateSteps - .associateWith{ it.businessPartner.legalEntity as IBaseLegalEntityDto} - .toMap() - val addressByTask = tasks - .associateWith{ it.businessPartner.legalEntity?.legalAddress as IBaseLogisticAddressDto} - .toMap() - - val errorsByRequest = requestValidationService.validateLegalEntityCreates(legalEntityByTask) { task -> task.businessPartner.legalEntity?.bpnLReference?.referenceValue } - val errorsByRequestAddress = requestValidationService.validateLegalEntityCreatesAddresses(addressByTask) { task -> task.businessPartner.legalEntity?.bpnLReference?.referenceValue } - - val legalEntityCreateTaskResults = legalEntitiesToCreateSteps - .map{ taskStep -> - if (errorsByRequest.containsKey(taskStep) || errorsByRequestAddress.containsKey(taskStep)) { - taskResultsForErrors(taskStep.taskId,errorsByRequest.getOrDefault(taskStep, mutableListOf()) + errorsByRequestAddress.getOrDefault(taskStep, mutableListOf()) ) - } else { - legalEntityCreateTaskResult(taskStep) - } - } - return legalEntityCreateTaskResults - } - - private fun taskResultsForErrors(errorTaskId: String, errors: Collection>) : TaskStepResultEntryDto { - - return TaskStepResultEntryDto( taskId = errorTaskId,errors = errors.map{TaskErrorDto(type= TaskErrorType.Unspecified,description = it.message)}) - } - - private fun legalEntityCreateTaskResult( task: TaskStepReservationEntryDto) : TaskStepResultEntryDto { - - val legalEnity = task.businessPartner.legalEntity - val legalName = legalEnity?.legalName - val legalAddress = legalEnity?.legalAddress - - return if ( legalEnity != null && legalName != null && legalAddress != null) { - val bpnLs = bpnIssuingService.issueLegalEntityBpns(1) - val legalEntityMetadataMap = metadataService.getMetadata(listOf(legalEnity)).toMapping() - val newLegalEntity= createLegalEntity(legalEnity, bpnLs[0], legalName, legalEntityMetadataMap) - // todo create address, changelogService and write to db - TaskStepResultEntryDto( - taskId = task.taskId, - businessPartner = BusinessPartnerFullDto(generic = task.businessPartner.generic) - ) - } else{ - TaskStepResultEntryDto( taskId = task.taskId, - errors = listOf(TaskErrorDto( - type= TaskErrorType.Unspecified, - description = "Legal name or legal address is empty") - ) - ) - } - } - - - private fun createLegalEntity( - legalEntityDto: LegalEntityDto, - bpnL: String, - legalNameValue: String, - metadataMap: BusinessPartnerBuildService.LegalEntityMetadataMapping - ): LegalEntity { - - // it has to be validated that the legalForm exits - val legalForm = legalEntityDto.legalForm?.let { metadataMap.legalForms[it]!! } - val legalName = Name( - value = legalNameValue, - shortName = legalEntityDto.legalShortName - ) - val newLegalEntity = LegalEntity( - bpn = bpnL, - legalName = legalName, - legalForm = legalForm, - currentness = Instant.now().truncatedTo(ChronoUnit.MICROS), - ) - updateLegalEntity(newLegalEntity, legalEntityDto, legalName,legalEntityDto.identifiers.map { toEntity(it, metadataMap.idTypes, newLegalEntity) }) - - return newLegalEntity - } - - private fun updateLegalEntity( - legalEntity: LegalEntity, - request: LegalEntityDto, - legalName: Name, - identifiers: List - ) { - - legalEntity.currentness = createCurrentnessTimestamp() - - legalEntity.legalName = legalName - - legalEntity.identifiers.clear() - legalEntity.states.clear() - legalEntity.classifications.clear() - - legalEntity.states.addAll(request.states.map { toEntity(it, legalEntity) }) - legalEntity.identifiers.addAll(identifiers) - legalEntity.classifications.addAll(request.classifications.map { toEntity(it, legalEntity) }.toSet()) - } - - private fun createCurrentnessTimestamp(): Instant { - return Instant.now().truncatedTo(ChronoUnit.MICROS) - } - - private fun toEntity(dto: org.eclipse.tractusx.orchestrator.api.model.LegalEntityState, legalEntity: LegalEntity): LegalEntityState { - return LegalEntityState( - description = dto.description, - validFrom = dto.validFrom, - validTo = dto.validTo, - type = dto.type, - legalEntity = legalEntity - ) - } - - private fun toEntity( - dto: org.eclipse.tractusx.orchestrator.api.model.LegalEntityIdentifierDto, - idTypes: Map, - partner: LegalEntity - ): LegalEntityIdentifier { - return LegalEntityIdentifier( - value = dto.value, - type = idTypes[dto.type]!!, - issuingBody = dto.issuingBody, - legalEntity = partner - ) - } - - private fun toEntity(dto: BusinessPartnerClassificationDto, partner: LegalEntity): LegalEntityClassification { - return LegalEntityClassification( - value = dto.value, - code = dto.code, - type = dto.type, - legalEntity = partner - ) - } - - private fun LegalEntityMetadataDto.toMapping() = - BusinessPartnerBuildService.LegalEntityMetadataMapping( - idTypes = idTypes.associateBy { it.technicalKey }, - legalForms = legalForms.associateBy { it.technicalKey } - ) -} \ No newline at end of file diff --git a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepBuildService.kt b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepBuildService.kt new file mode 100644 index 000000000..9f4f7fc8d --- /dev/null +++ b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepBuildService.kt @@ -0,0 +1,453 @@ +/******************************************************************************* + * 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.pool.service + +import jakarta.transaction.Transactional +import org.eclipse.tractusx.bpdm.common.dto.BusinessPartnerType +import org.eclipse.tractusx.bpdm.common.util.replace +import org.eclipse.tractusx.bpdm.pool.api.model.ChangelogType +import org.eclipse.tractusx.bpdm.pool.dto.AddressMetadataDto +import org.eclipse.tractusx.bpdm.pool.dto.ChangelogEntryCreateRequest +import org.eclipse.tractusx.bpdm.pool.dto.LegalEntityMetadataDto +import org.eclipse.tractusx.bpdm.pool.entity.* +import org.eclipse.tractusx.bpdm.pool.exception.BpdmValidationException +import org.eclipse.tractusx.bpdm.pool.repository.LegalEntityRepository +import org.eclipse.tractusx.bpdm.pool.repository.LogisticAddressRepository +import org.eclipse.tractusx.bpdm.pool.repository.SiteRepository +import org.eclipse.tractusx.orchestrator.api.model.* +import org.springframework.stereotype.Service +import java.time.Instant +import java.time.temporal.ChronoUnit + +@Service +class TaskStepBuildService( + private val metadataService: MetadataService, + private val bpnIssuingService: BpnIssuingService, + private val changelogService: PartnerChangelogService, + private val legalEntityRepository: LegalEntityRepository, + private val logisticAddressRepository: LogisticAddressRepository, + private val siteRepository: SiteRepository, +) { + + enum class CleaningError(val message: String) { + LEGAL_NAME_IS_NULL("Legal name is null"), + COUNTRY_CITY_IS_NULL("Country or city in physicalAddress is null"), + LEGAL_ENTITY_IS_NULL("Legal entity or BpnL Reference is null"), + LEGAL_ADDRESS_IS_NULL("Legal Address is null"), + LOGISTIC_ADDRESS_IS_NULL("Logistic Address or Physical Address is null"), + PHYSICAL_ADDRESS_IS_NULL("Physical Address is null"), + ALTERNATIVE_ADDRESS_DATA_IS_NULL("Country or city or deliveryServiceType or deliveryServiceNumber in alternativeAddress is null"), + MAINE_ADDRESS_IS_NULL("Main address is null"), + BPNS_IS_NULL("BpnS Reference is null"), + BPNA_IS_NULL("BpnA Reference is null"), + SITE_NAME_IS_NULL("Site name is null"), + INVALID_LOGISTIC_ADDRESS_BPN("Invalid Logistic Address BPN"), + INVALID_LEGAL_ENTITY_BPN("Invalid legal entity BPN"), + INVALID_SITE_BPN("Invalid site BPN") + + } + + @Transactional + fun upsertBusinessPartner(taskEntry: TaskStepReservationEntryDto): TaskStepResultEntryDto { + + // TODO associate generated BPN with BPN request identifier + val businessPartnerDto = taskEntry.businessPartner + var siteResult: SiteDto? = null + var addressResult: LogisticAddressDto? = null + + val legalEntity = upsertLegalEntity(businessPartnerDto.legalEntity) + var siteEntity: Site? = null + if (businessPartnerDto.site != null) { + siteEntity = upsertSite(businessPartnerDto.site, legalEntity) + siteResult = businessPartnerDto.site!!.copy( + bpnSReference = BpnReferenceDto(referenceValue = siteEntity.bpn, referenceType = BpnReferenceType.Bpn) + ) + } + if (businessPartnerDto.address != null) { + val addressEntity = upsertLogisticAddress(businessPartnerDto.address, legalEntity, siteEntity) + addressResult = businessPartnerDto.address!!.copy( + bpnAReference = BpnReferenceDto(referenceValue = addressEntity.bpn, referenceType = BpnReferenceType.Bpn) + ) + } + + return TaskStepResultEntryDto( + taskId = taskEntry.taskId, + businessPartner = BusinessPartnerFullDto( + generic = businessPartnerDto.generic, + legalEntity = businessPartnerDto.legalEntity!!.copy( + bpnLReference = BpnReferenceDto(referenceValue = legalEntity.bpn, referenceType = BpnReferenceType.Bpn) + ), + site = siteResult, + address = addressResult + ) + ) + } + + private fun upsertLogisticAddress( + addressDto: LogisticAddressDto?, + legalEntity: LegalEntity, + siteEntity: Site? + ): LogisticAddress { + + val bpnAReference = addressDto?.bpnAReference ?: throw BpdmValidationException(CleaningError.BPNA_IS_NULL.message) + + val isCreate = bpnAReference.referenceType == BpnReferenceType.BpnRequestIdentifier + val changelogType = + if (isCreate) ChangelogType.CREATE else ChangelogType.UPDATE + + val upsertAddress = if (isCreate) { + val bpnLA = bpnIssuingService.issueAddressBpns(1).single() + createLogisticAddressInternal(addressDto, bpnLA) + } else { + val addressMetadataMap = metadataService.getMetadata(listOf(addressDto)).toMapping() + + val updateAddress = logisticAddressRepository.findByBpn(bpnAReference.referenceValue) + if (updateAddress != null) { + updateLogisticAddress(updateAddress, addressDto, addressMetadataMap) + } else { + throw BpdmValidationException(CleaningError.INVALID_LOGISTIC_ADDRESS_BPN.message) + } + updateAddress + } + if (siteEntity != null) { + upsertAddress.site = siteEntity + } else { + upsertAddress.legalEntity = legalEntity + } + logisticAddressRepository.save(upsertAddress) + changelogService.createChangelogEntries( + listOf( + ChangelogEntryCreateRequest(upsertAddress.bpn, changelogType, BusinessPartnerType.ADDRESS) + ) + ) + + return upsertAddress + } + + private fun createLogisticAddress( + addressDto: LogisticAddressDto? + ): LogisticAddress { + + val bpnLA = bpnIssuingService.issueAddressBpns(1) + val newAddress = createLogisticAddressInternal(addressDto, bpnLA[0]) + changelogService.createChangelogEntries( + listOf( + ChangelogEntryCreateRequest(newAddress.bpn, ChangelogType.CREATE, BusinessPartnerType.ADDRESS) + ) + ) + + return newAddress + } + + private fun createLogisticAddressInternal( + dto: LogisticAddressDto?, + bpn: String + ): LogisticAddress { + + if (dto?.physicalPostalAddress == null) { + throw BpdmValidationException(CleaningError.LOGISTIC_ADDRESS_IS_NULL.message) + } + + val addressMetadataMap = metadataService.getMetadata(listOf(dto)).toMapping() + val address = LogisticAddress( + bpn = bpn, + legalEntity = null, + site = null, + physicalPostalAddress = createPhysicalAddress(dto.physicalPostalAddress!!, addressMetadataMap.regions), + alternativePostalAddress = dto.alternativePostalAddress?.let { createAlternativeAddress(it, addressMetadataMap.regions) }, + name = dto.name + ) + updateAddressIdentifiersAndStates(address, dto, addressMetadataMap.idTypes) + + return address + } + + private fun updateLogisticAddress(address: LogisticAddress, dto: LogisticAddressDto, metadataMap: BusinessPartnerBuildService.AddressMetadataMapping) { + + if (dto.physicalPostalAddress == null) { + + throw BpdmValidationException(CleaningError.PHYSICAL_ADDRESS_IS_NULL.message) + } + + address.name = dto.name + address.physicalPostalAddress = createPhysicalAddress(dto.physicalPostalAddress!!, metadataMap.regions) + address.alternativePostalAddress = dto.alternativePostalAddress?.let { createAlternativeAddress(it, metadataMap.regions) } + + updateAddressIdentifiersAndStates(address, dto, metadataMap.idTypes) + } + + private fun updateAddressIdentifiersAndStates( + address: LogisticAddress, + dto: LogisticAddressDto, + idTypes: Map + ) { + address.identifiers.apply { + clear() + addAll(dto.identifiers.map { + AddressIdentifier( + value = it.value, + type = idTypes[it.type]!!, + address = address + ) + }) + } + address.states.apply { + clear() + addAll(dto.states.map { + AddressState( + description = it.description, + validFrom = it.validFrom, + validTo = it.validTo, + type = it.type, + address = address + ) + }) + } + } + + private fun createAlternativeAddress(alternativeAddress: AlternativePostalAddressDto, regions: Map): AlternativePostalAddress { + + if (alternativeAddress.country == null || alternativeAddress.city == null || + alternativeAddress.deliveryServiceType == null || alternativeAddress.deliveryServiceNumber == null + ) { + + throw BpdmValidationException(CleaningError.ALTERNATIVE_ADDRESS_DATA_IS_NULL.message) + } + + return AlternativePostalAddress( + geographicCoordinates = alternativeAddress.geographicCoordinates?.let { GeographicCoordinate(it.latitude, it.longitude, it.altitude) }, + country = alternativeAddress.country!!, + administrativeAreaLevel1 = regions[alternativeAddress.administrativeAreaLevel1], + postCode = alternativeAddress.postalCode, + city = alternativeAddress.city!!, + deliveryServiceType = alternativeAddress.deliveryServiceType!!, + deliveryServiceNumber = alternativeAddress.deliveryServiceNumber!!, + deliveryServiceQualifier = alternativeAddress.deliveryServiceQualifier + ) + } + + private fun createPhysicalAddress(physicalAddress: PhysicalPostalAddressDto, regions: Map): PhysicalPostalAddress { + + if (physicalAddress.country == null || physicalAddress.city == null) { + throw BpdmValidationException(CleaningError.COUNTRY_CITY_IS_NULL.message) + } + + return PhysicalPostalAddress( + geographicCoordinates = physicalAddress.geographicCoordinates?.let { GeographicCoordinate(it.latitude, it.longitude, it.altitude) }, + country = physicalAddress.country!!, + administrativeAreaLevel1 = regions[physicalAddress.administrativeAreaLevel1], + administrativeAreaLevel2 = physicalAddress.administrativeAreaLevel2, + administrativeAreaLevel3 = physicalAddress.administrativeAreaLevel3, + postCode = physicalAddress.postalCode, + city = physicalAddress.city!!, + districtLevel1 = physicalAddress.district, + street = physicalAddress.street?.let { + Street( + name = it.name, + houseNumber = it.houseNumber, + milestone = it.milestone, + direction = it.direction + ) + }, + companyPostCode = physicalAddress.companyPostalCode, + industrialZone = physicalAddress.industrialZone, + building = physicalAddress.building, + floor = physicalAddress.floor, + door = physicalAddress.door + ) + } + + private fun createLegalEntity( + legalEntityDto: LegalEntityDto, + bpnL: String, + metadataMap: BusinessPartnerBuildService.LegalEntityMetadataMapping + ): LegalEntity { + + if (legalEntityDto.legalName == null) { + throw BpdmValidationException(CleaningError.LEGAL_NAME_IS_NULL.message) + } + + // it has to be validated that the legalForm exits + val legalForm = legalEntityDto.legalForm?.let { metadataMap.legalForms[it]!! } + val legalName = Name(value = legalEntityDto.legalName!!, shortName = legalEntityDto.legalShortName) + val newLegalEntity = LegalEntity( + bpn = bpnL, + legalName = legalName, + legalForm = legalForm, + currentness = Instant.now().truncatedTo(ChronoUnit.MICROS), + ) + updateLegalEntity(newLegalEntity, legalEntityDto, + legalEntityDto.identifiers.map { BusinessPartnerBuildService.toLegalEntityIdentifier(it, metadataMap.idTypes, newLegalEntity) }) + + return newLegalEntity + } + + private fun updateLegalEntity( + legalEntity: LegalEntity, + legalEntityDto: LegalEntityDto, + identifiers: List + ) { + val legalName = legalEntityDto.legalName ?: throw BpdmValidationException(CleaningError.LEGAL_NAME_IS_NULL.message) + + legalEntity.currentness = BusinessPartnerBuildService.createCurrentnessTimestamp() + + legalEntity.legalName = Name(value = legalName, shortName = legalEntityDto.legalShortName) + + legalEntity.identifiers.replace(identifiers) + + legalEntity.states.replace(legalEntityDto.states + .map { BusinessPartnerBuildService.toLegalEntityState(it, legalEntity) }) + + legalEntity.classifications.replace( + legalEntityDto.classifications + .map { BusinessPartnerBuildService.toLegalEntityClassification(it, legalEntity) }.toSet() + ) + } + + + fun upsertLegalEntity( + legalEntityDto: LegalEntityDto? + ): LegalEntity { + + val bpnLReference = legalEntityDto?.bpnLReference ?: throw BpdmValidationException(CleaningError.LEGAL_ENTITY_IS_NULL.message) + val legalAddress = legalEntityDto.legalAddress ?: throw BpdmValidationException(CleaningError.LEGAL_ADDRESS_IS_NULL.message) + + val isCreate = bpnLReference.referenceType == BpnReferenceType.BpnRequestIdentifier + val changelogType = + if (isCreate) ChangelogType.CREATE else ChangelogType.UPDATE + + val legalEntityMetadataMap = metadataService.getMetadata(listOf(legalEntityDto)).toMapping() + + val upsertLe = if (isCreate) { + val bpnL = bpnIssuingService.issueLegalEntityBpns(1).single() + val createdLe = createLegalEntity(legalEntityDto, bpnL, legalEntityMetadataMap) + val address = createLogisticAddress(legalAddress) + createdLe.legalAddress = address + address.legalEntity = createdLe + createdLe + } else { + + val updateLe = legalEntityRepository.findByBpn(bpnLReference.referenceValue) + if (updateLe != null) { + if (legalEntityDto.hasChanged == false) { + updateLegalEntity(updateLe, legalEntityDto, + legalEntityDto.identifiers.map { BusinessPartnerBuildService.toLegalEntityIdentifier(it, legalEntityMetadataMap.idTypes, updateLe) }) + val addressMetadataMap = metadataService.getMetadata(listOf(legalAddress)).toMapping() + updateLogisticAddress(updateLe.legalAddress, legalAddress, addressMetadataMap) + } + } else { + throw BpdmValidationException(CleaningError.INVALID_LEGAL_ENTITY_BPN.message) + } + updateLe + } + legalEntityRepository.save(upsertLe) + changelogService.createChangelogEntries( + listOf( + ChangelogEntryCreateRequest(upsertLe.bpn, changelogType, BusinessPartnerType.LEGAL_ENTITY) + ) + ) + + return upsertLe + } + + private fun upsertSite( + siteDto: SiteDto?, + legalEntity: LegalEntity + ): Site { + val bpnSReference = siteDto?.bpnSReference ?: throw BpdmValidationException(CleaningError.BPNS_IS_NULL.message) + val mainAddress = siteDto.mainAddress ?: throw BpdmValidationException(CleaningError.MAINE_ADDRESS_IS_NULL.message) + + val isCreate = bpnSReference.referenceType == BpnReferenceType.BpnRequestIdentifier + val changelogType = if (isCreate) ChangelogType.CREATE else ChangelogType.UPDATE + + val upsertSite = if (isCreate) { + val bpnS = bpnIssuingService.issueSiteBpns(1).single() + val createSite = createSite(siteDto, bpnS, legalEntity) + val address = createLogisticAddress(mainAddress) + createSite.mainAddress = address + address.site = createSite + createSite + } else { + + val updateSite = siteRepository.findByBpn(siteDto.bpnSReference?.referenceValue!!) + if (updateSite != null) { + if (siteDto.hasChanged == false) { + updateSite(updateSite, siteDto) + val addressMetadataMap = metadataService.getMetadata(listOf(mainAddress)).toMapping() + updateLogisticAddress(updateSite.mainAddress, mainAddress, addressMetadataMap) + } + } else { + throw BpdmValidationException(CleaningError.INVALID_SITE_BPN.message) + } + updateSite + } + siteRepository.save(upsertSite) + changelogService.createChangelogEntries( + listOf( + ChangelogEntryCreateRequest(upsertSite.bpn, changelogType, BusinessPartnerType.SITE) + ) + ) + + return upsertSite + } + + private fun createSite( + siteDto: SiteDto, + bpnS: String, + partner: LegalEntity + ): Site { + + val name = siteDto.name ?: throw BpdmValidationException(CleaningError.SITE_NAME_IS_NULL.message) + + val site = Site( + bpn = bpnS, + name = name, + legalEntity = partner, + ) + + site.states.addAll(siteDto.states + .map { BusinessPartnerBuildService.toSiteState(it, site) }) + + return site + } + + private fun updateSite(site: Site, siteDto: SiteDto) { + + val name = siteDto.name ?: throw BpdmValidationException(CleaningError.SITE_NAME_IS_NULL.message) + + site.name = name + + site.states.clear() + site.states.addAll(siteDto.states + .map { BusinessPartnerBuildService.toSiteState(it, site) }) + } + + private fun AddressMetadataDto.toMapping() = + BusinessPartnerBuildService.AddressMetadataMapping( + idTypes = idTypes.associateBy { it.technicalKey }, + regions = regions.associateBy { it.regionCode } + ) + + private fun LegalEntityMetadataDto.toMapping() = + BusinessPartnerBuildService.LegalEntityMetadataMapping( + idTypes = idTypes.associateBy { it.technicalKey }, + legalForms = legalForms.associateBy { it.technicalKey } + ) +} \ No newline at end of file diff --git a/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepFetchAndReserveService.kt b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepFetchAndReserveService.kt new file mode 100644 index 000000000..37373c65d --- /dev/null +++ b/bpdm-pool/src/main/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepFetchAndReserveService.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.pool.service + +import mu.KotlinLogging +import org.eclipse.tractusx.bpdm.common.dto.IBaseLegalEntityDto +import org.eclipse.tractusx.bpdm.common.dto.IBaseLogisticAddressDto +import org.eclipse.tractusx.bpdm.pool.api.model.response.ErrorInfo +import org.eclipse.tractusx.bpdm.pool.api.model.response.LegalEntityCreateError +import org.eclipse.tractusx.bpdm.pool.exception.BpdmValidationException +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 + +@Service +class TaskStepFetchAndReserveService( + private val orchestrationClient: OrchestrationApiClient, + private val taskStepBuildService: TaskStepBuildService, + private val requestValidationService: RequestValidationService, +) { + private val logger = KotlinLogging.logger { } + + @Scheduled(cron = "\${bpdm.pool-orchestrator.golden-record-scheduler-cron-expr:-}", zone = "UTC") + fun fetchAndReserve() { + try { + logger.info { "Starting polling for cleaning tasks from Orchestrator..." } + val reservationRequest = TaskStepReservationRequest(step = TaskStep.PoolSync, amount = 10) + val taskStepReservation = orchestrationClient.goldenRecordTasks.reserveTasksForStep(reservationRequest = reservationRequest) + + logger.info { "${taskStepReservation.reservedTasks.size} tasks found for cleaning. Proceeding with cleaning..." } + + if (taskStepReservation.reservedTasks.isNotEmpty()) { + val taskResults = upsertGoldenRecordIntoPool(taskStepReservation.reservedTasks) + orchestrationClient.goldenRecordTasks.resolveStepResults(TaskStepResultRequest(step = TaskStep.PoolSync, results = taskResults)) + } + logger.info { "Cleaning tasks processing completed for this iteration." } + } catch (ex: Throwable) { + logger.error(ex) { "Error while processing cleaning task" } + } + + } + + fun upsertGoldenRecordIntoPool(taskEntries: List): List { + + //TODO Implement validation for sites, ... + val validationStepErrorsByEntry = validateLegalEntityCreateTasks(taskEntries) + + return taskEntries.map { + + val existingEntryError = validationStepErrorsByEntry.get(it) + existingEntryError ?: businessPartnerTaskResult(it) + } + } + + fun businessPartnerTaskResult(taskStep: TaskStepReservationEntryDto): TaskStepResultEntryDto { + + return try { + taskStepBuildService.upsertBusinessPartner(taskStep) + } catch (ex: BpdmValidationException) { + TaskStepResultEntryDto( + taskId = taskStep.taskId, + errors = listOf( + TaskErrorDto( + type = TaskErrorType.Unspecified, + description = ex.message ?: "" + ) + ) + ) + } + } + + private fun validateLegalEntityCreateTasks( + tasks: List + ): Map { + + val isTaskCreateLegalEntity = + { task: TaskStepReservationEntryDto -> task.businessPartner.legalEntity?.bpnLReference?.referenceType == BpnReferenceType.BpnRequestIdentifier } + + val legalEntitiesToCreateSteps = tasks + .filter { isTaskCreateLegalEntity(it) } + + val legalEntityByTask = legalEntitiesToCreateSteps + .associateWith { it.businessPartner.legalEntity as IBaseLegalEntityDto } + .toMap() + val addressByTask = tasks + .filter { it.businessPartner.legalEntity?.legalAddress != null } + .associateWith { it.businessPartner.legalEntity?.legalAddress as IBaseLogisticAddressDto } + .toMap() + + val errorsByRequest = + requestValidationService.validateLegalEntityCreates(legalEntityByTask) { task -> task.businessPartner.legalEntity?.bpnLReference?.referenceValue } + val errorsByRequestAddress = + requestValidationService.validateLegalEntityCreatesAddresses(addressByTask) { task -> task.businessPartner.legalEntity?.bpnLReference?.referenceValue } + + val legalEntityCreateTaskResults = legalEntitiesToCreateSteps + .map { taskStep -> + taskStep to taskStepResultEntryDto(taskStep, errorsByRequest, errorsByRequestAddress) + }.toMap() + .filterValues { it != null } + return legalEntityCreateTaskResults + } + + private fun taskStepResultEntryDto( + taskStep: TaskStepReservationEntryDto, + errorsByRequest: Map>>, + errorsByRequestAddress: Map>> + ) = if (errorsByRequest.containsKey(taskStep) || errorsByRequestAddress.containsKey(taskStep)) { + taskResultsForErrors( + taskStep.taskId, + errorsByRequest.getOrDefault(taskStep, mutableListOf()) + errorsByRequestAddress.getOrDefault(taskStep, mutableListOf()) + ) + } else { + null + } + + private fun taskResultsForErrors(taskId: String, errors: Collection>): TaskStepResultEntryDto { + + return TaskStepResultEntryDto(taskId = taskId, errors = errors.map { TaskErrorDto(type = TaskErrorType.Unspecified, description = it.message) }) + } + +} \ No newline at end of file diff --git a/bpdm-pool/src/main/resources/application.properties b/bpdm-pool/src/main/resources/application.properties index 65def6447..11382333e 100644 --- a/bpdm-pool/src/main/resources/application.properties +++ b/bpdm-pool/src/main/resources/application.properties @@ -57,6 +57,7 @@ bpdm.opensearch.max-page=20 # Special value "-" disables scheduling. See javadoc of org.springframework.scheduling.support.CronExpression.parse for format. bpdm.opensearch.export-scheduler-cron-expr=- bpdm.opensearch.refresh-on-write=false +bpdm.pool-orchestrator.golden-record-scheduler-cron-expr=- #Datasource configuration bpdm.datasource.host=localhost spring.datasource.url=jdbc:postgresql://${bpdm.datasource.host}:5432/bpdm diff --git a/bpdm-pool/src/test/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepFetchAndReserveServiceTest.kt b/bpdm-pool/src/test/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepFetchAndReserveServiceTest.kt new file mode 100644 index 000000000..b0b8c134f --- /dev/null +++ b/bpdm-pool/src/test/kotlin/org/eclipse/tractusx/bpdm/pool/service/TaskStepFetchAndReserveServiceTest.kt @@ -0,0 +1,127 @@ +package org.eclipse.tractusx.bpdm.pool.service + +import com.neovisionaries.i18n.CountryCode +import org.assertj.core.api.Assertions.assertThat +import org.eclipse.tractusx.bpdm.pool.Application +import org.eclipse.tractusx.bpdm.pool.api.client.PoolClientImpl +import org.eclipse.tractusx.bpdm.pool.service.TaskStepBuildService.CleaningError +import org.eclipse.tractusx.bpdm.pool.util.PostgreSQLContextInitializer +import org.eclipse.tractusx.bpdm.pool.util.TestHelpers +import org.eclipse.tractusx.orchestrator.api.model.* +import org.eclipse.tractusx.orchestrator.api.model.BpnReferenceType.BpnRequestIdentifier +import org.junit.jupiter.api.BeforeEach +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 org.springframework.test.context.ContextConfiguration + +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = [Application::class] +) +@ActiveProfiles("test") +@ContextConfiguration(initializers = [PostgreSQLContextInitializer::class]) +class TaskStepFetchAndReserveServiceTest @Autowired constructor( + val cleaningStepService: TaskStepFetchAndReserveService, + val testHelpers: TestHelpers, + val poolClient: PoolClientImpl +) { + + + @BeforeEach + fun beforeEach() { + testHelpers.truncateDbTables() + testHelpers.createTestMetadata() + } + + @Test + fun `upsert Golden Record into pool with empty legal entity`() { + + val fullBpWithLegalEntity = minFullBusinessPartner().copy( + legalEntity = emptyLegalEntity() + ) + + val result = cleanStep(taskId = "TASK_1", businessPartner = fullBpWithLegalEntity) + assertTaskError(result[0], "TASK_1", CleaningError.LEGAL_ENTITY_IS_NULL) + } + + @Test + fun `upsert Golden Record into pool with legal entity without legal name to create`() { + + val fullBpWithLegalEntity = minFullBusinessPartner().copy( + legalEntity = emptyLegalEntity().copy( + bpnLReference = BpnReferenceDto(referenceValue = "123", referenceType = BpnRequestIdentifier), + legalAddress = LogisticAddressDto() + ) + ) + + val result = cleanStep(taskId = "TASK_1", businessPartner = fullBpWithLegalEntity) + assertTaskError(result[0], "TASK_1", CleaningError.LEGAL_NAME_IS_NULL) + } + + @Test + fun `upsert Golden Record into pool with legal entity to create`() { + + val fullBpWithLegalEntity = minFullBusinessPartner().copy( + legalEntity = minValidLegalEntity( + BpnReferenceDto(referenceValue = "123", referenceType = BpnRequestIdentifier), + BpnReferenceDto(referenceValue = "222", referenceType = BpnRequestIdentifier) + ) + ) + val resultSteps = cleanStep(taskId = "TASK_1", businessPartner = fullBpWithLegalEntity) + assertThat(resultSteps[0].taskId).isEqualTo("TASK_1") + assertThat(resultSteps[0].errors.size).isEqualTo(0) + + val createdLegalEntity = poolClient.legalEntities.getLegalEntity(resultSteps[0].businessPartner?.legalEntity?.bpnLReference?.referenceValue!!) + assertThat(createdLegalEntity.legalAddress.bpnLegalEntity).isNotNull() + } + + fun cleanStep(taskId: String, businessPartner: BusinessPartnerFullDto): List { + + val steps = singleTaskStep(taskId = "TASK_1", businessPartner = businessPartner) + return cleaningStepService.upsertGoldenRecordIntoPool(steps) + } + + fun singleTaskStep(taskId: String, businessPartner: BusinessPartnerFullDto): List { + + return listOf( + TaskStepReservationEntryDto( + taskId = taskId, + businessPartner = businessPartner + ) + ) + } + + fun minFullBusinessPartner(): BusinessPartnerFullDto { + + return BusinessPartnerFullDto(generic = BusinessPartnerGenericDto()) + } + + fun emptyLegalEntity(): LegalEntityDto { + + return LegalEntityDto() + } + + fun minValidLegalEntity(bpnLReference: BpnReferenceDto, bpnAReference: BpnReferenceDto): LegalEntityDto { + + return LegalEntityDto( + bpnLReference = bpnLReference, + legalName = "legalName_" + bpnLReference.referenceValue, + legalAddress = LogisticAddressDto( + bpnAReference = bpnAReference, + physicalPostalAddress = PhysicalPostalAddressDto( + country = CountryCode.DE, + city = "City" + bpnLReference.referenceValue + ) + ) + ) + } + + fun assertTaskError(step: TaskStepResultEntryDto, taskId: String, error: CleaningError) { + + assertThat(step.taskId).isEqualTo(taskId) + assertThat(step.errors.size).isEqualTo(1) + assertThat(step.errors[0].description).isEqualTo(error.message) + + } +} \ No newline at end of file