diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/BusinessPartnerController.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/BusinessPartnerController.kt index 4b8efeed3..7e3708ff9 100644 --- a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/BusinessPartnerController.kt +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/BusinessPartnerController.kt @@ -45,7 +45,7 @@ class BusinessPartnerController( if (businessPartners.size > apiConfigProperties.upsertLimit || businessPartners.map { it.externalId }.containsDuplicates()) { return ResponseEntity(HttpStatus.BAD_REQUEST) } - val result = businessPartnerService.upsertBusinessPartnersInput(businessPartners) + val result = businessPartnerService.upsertBusinessPartnersInput(businessPartners.toList()) return ResponseEntity.ok(result) } diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/exception/BpdmInvalidStateRequestException.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/exception/BpdmInvalidStateRequestException.kt new file mode 100644 index 000000000..149670e72 --- /dev/null +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/exception/BpdmInvalidStateRequestException.kt @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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.gate.exception + +import org.springframework.http.HttpStatus +import org.springframework.web.bind.annotation.ResponseStatus + +@ResponseStatus(HttpStatus.BAD_REQUEST) +class BpdmInvalidStateRequestException( + msg: String +) : RuntimeException(msg) \ No newline at end of file diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStateRepository.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStateRepository.kt index 8f4eb47df..fb8079b05 100644 --- a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStateRepository.kt +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStateRepository.kt @@ -51,8 +51,9 @@ interface SharingStateRepository : PagingAndSortingRepository, businessPartnerType: BusinessPartnerType): Collection fun findBySharingStateType(sharingStateType: SharingStateType): Set + fun findBySharingStateTypeAndTaskIdNotNull(sharingStateType: SharingStateType): Set } \ No newline at end of file diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/AddressPersistenceService.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/AddressPersistenceService.kt index c35e1670c..c0cd71528 100644 --- a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/AddressPersistenceService.kt +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/AddressPersistenceService.kt @@ -24,7 +24,6 @@ import org.eclipse.tractusx.bpdm.common.dto.BusinessPartnerType import org.eclipse.tractusx.bpdm.common.model.StageType import org.eclipse.tractusx.bpdm.common.util.replace import org.eclipse.tractusx.bpdm.gate.api.model.ChangelogType -import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateType import org.eclipse.tractusx.bpdm.gate.api.model.request.AddressGateInputRequest import org.eclipse.tractusx.bpdm.gate.api.model.request.AddressGateOutputRequest import org.eclipse.tractusx.bpdm.gate.entity.ChangelogEntry @@ -75,10 +74,12 @@ class AddressPersistenceService( } ?: run { gateAddressRepository.save(fullAddress) - sharingStateService.upsertSharingState(address.toSharingStateDTO()) saveChangelog(address.externalId, ChangelogType.CREATE, dataType) } } + + val initRequests = addresses.map { SharingStateService.SharingStateIdentifierDto(it.externalId, BusinessPartnerType.ADDRESS ) } + sharingStateService.setInitial(initRequests) } private fun updateAddress(address: LogisticAddress, changeAddress: AddressGateInputRequest, legalEntityRecord: LegalEntity?, siteRecord: Site?) { @@ -127,8 +128,15 @@ class AddressPersistenceService( saveChangelog(address.externalId, ChangelogType.CREATE, dataType) } } - sharingStateService.upsertSharingState(address.toSharingStateDTO(SharingStateType.Success)) } + + val successRequests = addresses.map { + SharingStateService.SuccessRequest( + SharingStateService.SharingStateIdentifierDto(it.externalId, BusinessPartnerType.ADDRESS), + it.bpn + ) + } + sharingStateService.setSuccess(successRequests) } private fun updateAddressOutput(address: LogisticAddress, changeAddress: AddressGateOutputRequest, legalEntityRecord: LegalEntity?, siteRecord: Site?) { diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/BusinessPartnerService.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/BusinessPartnerService.kt index 245de7e6c..970d508ce 100644 --- a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/BusinessPartnerService.kt +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/BusinessPartnerService.kt @@ -32,8 +32,8 @@ import org.eclipse.tractusx.bpdm.gate.api.model.request.BusinessPartnerInputRequ import org.eclipse.tractusx.bpdm.gate.api.model.request.BusinessPartnerOutputRequest import org.eclipse.tractusx.bpdm.gate.api.model.response.BusinessPartnerInputDto import org.eclipse.tractusx.bpdm.gate.api.model.response.BusinessPartnerOutputDto -import org.eclipse.tractusx.bpdm.gate.api.model.response.SharingStateDto import org.eclipse.tractusx.bpdm.gate.entity.ChangelogEntry +import org.eclipse.tractusx.bpdm.gate.entity.SharingState import org.eclipse.tractusx.bpdm.gate.entity.generic.* import org.eclipse.tractusx.bpdm.gate.exception.BpdmMissingStageException import org.eclipse.tractusx.bpdm.gate.repository.ChangelogRepository @@ -59,15 +59,15 @@ class BusinessPartnerService( ) { @Transactional - fun upsertBusinessPartnersInput(dtos: Collection): Collection { + fun upsertBusinessPartnersInput(dtos: List): List { val entities = dtos.map { dto -> businessPartnerMappings.toBusinessPartnerInput(dto) } - return upsertBusinessPartnersInput(entities).map(businessPartnerMappings::toBusinessPartnerInputDto) + return upsertBusinessPartnersInputFromCandidates(entities).map(businessPartnerMappings::toBusinessPartnerInputDto) } @Transactional fun upsertBusinessPartnersOutput(dtos: Collection): Collection { val entities = dtos.map { dto -> businessPartnerMappings.toBusinessPartnerOutput(dto) } - return upsertBusinessPartnersOutput(entities).map(businessPartnerMappings::toBusinessPartnerOutputDto) + return upsertBusinessPartnersOutputFromCandidates(entities).map(businessPartnerMappings::toBusinessPartnerOutputDto) } fun getBusinessPartnersInput(pageRequest: PageRequest, externalIds: Collection?): PageDto { @@ -82,27 +82,25 @@ class BusinessPartnerService( .toPageDto(businessPartnerMappings::toBusinessPartnerOutputDto) } - private fun upsertBusinessPartnersInput(entityCandidates: List): List { + + private fun upsertBusinessPartnersInputFromCandidates(entityCandidates: List): List { val resolutionResults = resolveCandidatesForStage(entityCandidates, StageType.Input) saveChangelog(resolutionResults) val partners = resolutionResults.map { it.businessPartner } val orchestratorBusinessPartnersDto = resolutionResults.map { orchestratorMappings.toBusinessPartnerGenericDto(it.businessPartner) } - partners.forEach { entity -> - initSharingState(entity) - } - val taskCreateResponse = createGoldenRecordTasks(orchestratorBusinessPartnersDto) + val taskIds = createGoldenRecordTasks(orchestratorBusinessPartnersDto).createdTasks.map { it.taskId } - for (i in partners.indices) { - updateSharingState(partners[i].externalId, taskCreateResponse.createdTasks[i]) - } + val pendingRequests = partners.zip(taskIds) + .map { (partner, taskId) -> SharingStateService.PendingRequest(SharingStateService.SharingStateIdentifierDto(partner.externalId, BusinessPartnerType.ADDRESS), taskId) } + sharingStateService.setPending(pendingRequests) return businessPartnerRepository.saveAll(partners) } - private fun upsertBusinessPartnersOutput(entityCandidates: List): List { + private fun upsertBusinessPartnersOutputFromCandidates(entityCandidates: List): List { val externalIds = entityCandidates.map { it.externalId } assertInputStageExists(externalIds) @@ -110,7 +108,12 @@ class BusinessPartnerService( saveChangelog(resolutionResults) - return businessPartnerRepository.saveAll(resolutionResults.map { it.businessPartner }) + val partners = resolutionResults.map { it.businessPartner } + + val successRequests = partners.map { SharingStateService.SuccessRequest(SharingStateService.SharingStateIdentifierDto(it.externalId, BusinessPartnerType.ADDRESS), it.bpnA!!) } + sharingStateService.setSuccess(successRequests) + + return businessPartnerRepository.saveAll(partners) } private fun getBusinessPartners(pageRequest: PageRequest, externalIds: Collection?, stage: StageType): Page { @@ -120,11 +123,6 @@ class BusinessPartnerService( } } - private fun initSharingState(entity: BusinessPartner) { - // TODO make businessPartnerType optional - sharingStateService.upsertSharingState(SharingStateDto(BusinessPartnerType.GENERIC, entity.externalId)) - } - private fun saveChangelog(resolutionResults: Collection) { resolutionResults.forEach { result -> if (result.wasResolved) @@ -233,59 +231,33 @@ class BusinessPartnerService( ) } - private fun updateSharingState(externalId: String, stateDto: TaskClientStateDto) { - - val errorMessage = if (stateDto.processingState.errors.isNotEmpty()) stateDto.processingState.errors.joinToString(" // ") { it.description } else null - val errorCode = if (stateDto.processingState.errors.isNotEmpty()) BusinessPartnerSharingError.SharingProcessError else null - - sharingStateService.upsertSharingState( - SharingStateDto( - BusinessPartnerType.ADDRESS, - externalId, - sharingStateType = orchestratorMappings.toSharingStateType(stateDto.processingState.resultState), - sharingErrorCode = errorCode, - sharingErrorMessage = errorMessage, - taskId = stateDto.taskId - ) - ) - } - @Scheduled(cron = "\${cleaningService.pollingCron:-}", zone = "UTC") + @Transactional fun finishCleaningTask() { - - var validBusinessPartner: List = emptyList() - - val sharingStates = sharingStateRepository.findBySharingStateType(SharingStateType.Pending) - val nonNullTaskIds = sharingStates.mapNotNull { it.taskId } - - val taskStates = orchestrationApiClient.goldenRecordTasks.searchTaskStates(TaskStateRequest(nonNullTaskIds)) + val sharingStates = sharingStateRepository.findBySharingStateTypeAndTaskIdNotNull(SharingStateType.Pending) + val tasks = orchestrationApiClient.goldenRecordTasks.searchTaskStates(TaskStateRequest(sharingStates.map { it.taskId!! })).tasks val sharingStateMap = sharingStates.associateBy { it.taskId } - //Task verification and map - taskStates.tasks.forEach { task -> + val taskStatesByResult = tasks + .map { Pair(it, sharingStateMap[it.taskId]!!) } + .groupBy { (task, _) -> task.processingState.resultState } - val relatedSharingState = sharingStateMap[task.taskId] + val businessPartnersToUpsert = taskStatesByResult[ResultState.Success]?.map { (task, sharingState) -> + orchestratorMappings.toBusinessPartner(task.businessPartnerResult!!, sharingState.externalId) + } ?: emptyList() + upsertBusinessPartnersOutputFromCandidates(businessPartnersToUpsert) - // Check if relatedSharingState exists - if (relatedSharingState != null) { - if (task.processingState.resultState == ResultState.Success) { - val businessPartner = orchestratorMappings.toBusinessPartner(task.businessPartnerResult!!, relatedSharingState.externalId) - validBusinessPartner = validBusinessPartner.plus(businessPartner) - // Set Sharing State to Success - updateSharingState(businessPartner.externalId, task) - } else if (task.processingState.resultState == ResultState.Error) { - // Set related Sharing State Type as Error - updateSharingState(relatedSharingState.externalId, task) - } - } - } + val errorRequests = taskStatesByResult[ResultState.Error]?.map { (task, sharingState) -> + SharingStateService.ErrorRequest( + SharingStateService.SharingStateIdentifierDto(sharingState.externalId, sharingState.businessPartnerType), + BusinessPartnerSharingError.SharingProcessError, + if (task.processingState.errors.isNotEmpty()) task.processingState.errors.joinToString(" // ") { it.description } else null + ) + } ?: emptyList() + sharingStateService.setError(errorRequests) - //If it is cleaned, upsert Output - if (validBusinessPartner.isNotEmpty()) { - upsertBusinessPartnersOutput(validBusinessPartner) - } } } diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/LegalEntityPersistenceService.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/LegalEntityPersistenceService.kt index 17cd08b99..e040a05df 100644 --- a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/LegalEntityPersistenceService.kt +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/LegalEntityPersistenceService.kt @@ -25,7 +25,6 @@ import org.eclipse.tractusx.bpdm.common.exception.BpdmNotFoundException import org.eclipse.tractusx.bpdm.common.model.StageType import org.eclipse.tractusx.bpdm.common.util.replace import org.eclipse.tractusx.bpdm.gate.api.model.ChangelogType -import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateType import org.eclipse.tractusx.bpdm.gate.api.model.request.LegalEntityGateInputRequest import org.eclipse.tractusx.bpdm.gate.api.model.request.LegalEntityGateOutputRequest import org.eclipse.tractusx.bpdm.gate.entity.AddressState @@ -75,9 +74,10 @@ class LegalEntityPersistenceService( ?: run { gateLegalEntityRepository.save(fullLegalEntity) saveChangelog(legalEntity.externalId, ChangelogType.CREATE, datatype) - sharingStateService.upsertSharingState(legalEntity.toSharingStateDTO()) } } + val initRequests = legalEntities.map { SharingStateService.SharingStateIdentifierDto(it.externalId, BusinessPartnerType.LEGAL_ENTITY ) } + sharingStateService.setInitial(initRequests) } //Creates Changelog for both Legal Entity and Logistic Address when they are created or updated @@ -156,8 +156,15 @@ class LegalEntityPersistenceService( saveChangelog(legalEntity.externalId, ChangelogType.CREATE, datatype) } } - sharingStateService.upsertSharingState(legalEntity.toSharingStateDTO(SharingStateType.Success)) } + + val successRequests = legalEntities.map { + SharingStateService.SuccessRequest( + SharingStateService.SharingStateIdentifierDto(it.externalId, BusinessPartnerType.LEGAL_ENTITY), + it.bpn + ) + } + sharingStateService.setSuccess(successRequests) } private fun updateLegalEntityOutput( diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/SharingStateService.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/SharingStateService.kt index a58d4d574..fab6b8dd3 100644 --- a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/SharingStateService.kt +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/SharingStateService.kt @@ -22,55 +22,50 @@ package org.eclipse.tractusx.bpdm.gate.service import org.eclipse.tractusx.bpdm.common.dto.BusinessPartnerType import org.eclipse.tractusx.bpdm.common.dto.request.PaginationRequest import org.eclipse.tractusx.bpdm.common.dto.response.PageDto +import org.eclipse.tractusx.bpdm.common.service.toPageDto +import org.eclipse.tractusx.bpdm.gate.api.exception.BusinessPartnerSharingError +import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateType import org.eclipse.tractusx.bpdm.gate.api.model.response.SharingStateDto import org.eclipse.tractusx.bpdm.gate.entity.SharingState +import org.eclipse.tractusx.bpdm.gate.exception.BpdmInvalidStateRequestException import org.eclipse.tractusx.bpdm.gate.repository.SharingStateRepository import org.eclipse.tractusx.bpdm.gate.repository.SharingStateRepository.Specs.byBusinessPartnerType import org.eclipse.tractusx.bpdm.gate.repository.SharingStateRepository.Specs.byExternalIdsIn import org.springframework.data.domain.PageRequest import org.springframework.data.jpa.domain.Specification import org.springframework.stereotype.Service +import java.time.LocalDateTime + +const val ERROR_MISSING_CODE = "Request for Error state but no error code specified." +const val ERROR_SUCCESS = "Request for Success state but no BPN specified." @Service class SharingStateService(private val stateRepository: SharingStateRepository) { + /** + * Upsert fixed sharing state based on given DTO + */ fun upsertSharingState(request: SharingStateDto) { + val sharingState = getOrCreate(request.externalId, request.businessPartnerType) - val sharingState = this.stateRepository.findByExternalIdAndBusinessPartnerType(request.externalId, request.businessPartnerType) - if (sharingState == null) { - insertSharingState(request) - } else { - updateSharingState(sharingState, request) - } - } + when (request.sharingStateType) { + SharingStateType.Pending -> setPending(sharingState, startTimeOverwrite = request.sharingProcessStarted) - private fun insertSharingState(dto: SharingStateDto) { - - this.stateRepository.save( - SharingState( - externalId = dto.externalId, - businessPartnerType = dto.businessPartnerType, - sharingStateType = dto.sharingStateType, - sharingErrorCode = dto.sharingErrorCode, - sharingErrorMessage = dto.sharingErrorMessage, - bpn = dto.bpn, - sharingProcessStarted = dto.sharingProcessStarted, - taskId = dto.taskId + SharingStateType.Success -> setSuccess( + sharingState = sharingState, + bpn = request.bpn ?: throw BpdmInvalidStateRequestException(ERROR_SUCCESS), + startTimeOverwrite = request.sharingProcessStarted ) - ) - } - private fun updateSharingState(entity: SharingState, dto: SharingStateDto) { + SharingStateType.Error -> setError( + sharingState = sharingState, + sharingErrorCode = request.sharingErrorCode ?: throw BpdmInvalidStateRequestException(ERROR_MISSING_CODE), + sharingErrorMessage = request.sharingErrorMessage, + startTimeOverwrite = request.sharingProcessStarted + ) - entity.sharingStateType = dto.sharingStateType - entity.sharingErrorCode = dto.sharingErrorCode - entity.sharingErrorMessage = dto.sharingErrorMessage - entity.bpn = dto.bpn - entity.taskId = dto.taskId - if (dto.sharingProcessStarted != null) { - entity.sharingProcessStarted = dto.sharingProcessStarted + SharingStateType.Initial -> setInitial(sharingState) } - this.stateRepository.save(entity) } fun findSharingStates( @@ -79,11 +74,11 @@ class SharingStateService(private val stateRepository: SharingStateRepository) { externalIds: Collection? ): PageDto { - val spec = Specification.allOf(byBusinessPartnerType(businessPartnerType), byExternalIdsIn(externalIds)) val pageRequest = PageRequest.of(paginationRequest.page, paginationRequest.size) - val page = stateRepository.findAll(spec, pageRequest) + val spec = Specification.allOf(byExternalIdsIn(externalIds), byBusinessPartnerType(businessPartnerType)) + val sharingStatePage = stateRepository.findAll(spec, pageRequest) - return page.toDto(page.content.map { + return sharingStatePage.toPageDto { SharingStateDto( externalId = it.externalId, businessPartnerType = it.businessPartnerType, @@ -94,7 +89,133 @@ class SharingStateService(private val stateRepository: SharingStateRepository) { sharingProcessStarted = it.sharingProcessStarted, taskId = it.taskId ) - }) + } + } + + fun setInitial(sharingStateIds: List): List { + val sharingStates = getOrCreate(sharingStateIds) + return sharingStates.map { setInitial(it) } + } + + fun setSuccess(successRequests: List): List { + val sharingStates = getOrCreate(successRequests.map { it.sharingStateId }) + return sharingStates + .zip(successRequests) + .map { (sharingState, request) -> setSuccess(sharingState, request.bpn, request.startTimeOverwrite) } + } + + fun setPending(pendingRequests: List): List { + val sharingStates = getOrCreate(pendingRequests.map { it.sharingStateId }) + return sharingStates + .zip(pendingRequests) + .map { (sharingState, request) -> setPending(sharingState, request.startTimeOverwrite) } + } + + fun setError(errorRequests: List): List{ + val sharingStates = getOrCreate(errorRequests.map { it.sharingStateId }) + + return sharingStates + .zip(errorRequests) + .map { (sharingState, request) -> setError(sharingState, request.errorCode, request.errorMessage, request.startTimeOverwrite) } + } + + private fun setInitial(sharingState: SharingState): SharingState { + sharingState.sharingStateType = SharingStateType.Initial + sharingState.sharingErrorCode = null + sharingState.sharingErrorMessage = null + sharingState.sharingProcessStarted = null + + return stateRepository.save(sharingState) + } + + private fun setSuccess(sharingState: SharingState, bpn: String, startTimeOverwrite: LocalDateTime? = null): SharingState { + + sharingState.sharingStateType = SharingStateType.Success + sharingState.sharingErrorCode = null + sharingState.sharingErrorMessage = null + sharingState.bpn = bpn + sharingState.sharingProcessStarted = startTimeOverwrite ?: sharingState.sharingProcessStarted ?: LocalDateTime.now() + + return stateRepository.save(sharingState) + } + + private fun setPending(sharingState: SharingState, startTimeOverwrite: LocalDateTime? = null): SharingState { + sharingState.sharingStateType = SharingStateType.Pending + sharingState.sharingErrorCode = null + sharingState.sharingErrorMessage = null + sharingState.sharingProcessStarted = startTimeOverwrite ?: sharingState.sharingProcessStarted ?: LocalDateTime.now() + + return stateRepository.save(sharingState) + } + + private fun setError( + sharingState: SharingState, + sharingErrorCode: BusinessPartnerSharingError, + sharingErrorMessage: String? = null, + startTimeOverwrite: LocalDateTime? = null + ): SharingState { + sharingState.sharingStateType = SharingStateType.Error + sharingState.sharingErrorCode = sharingErrorCode + sharingState.sharingErrorMessage = sharingErrorMessage + sharingState.sharingProcessStarted = startTimeOverwrite ?: sharingState.sharingProcessStarted ?: LocalDateTime.now() + + return stateRepository.save(sharingState) + } + + private fun getOrCreate(sharingStateIdentifiers: List): List{ + val identifiersByType = sharingStateIdentifiers.groupBy { it.businessPartnerType } + + val sharingStates = identifiersByType.flatMap { entry -> getOrCreate(entry.value.map { it.externalId }, entry.key) } + val sharingStatesByExternalId = sharingStates.associateBy { it.externalId } + + return sharingStateIdentifiers.map { sharingStatesByExternalId[it.externalId]!! } + } + + + private fun getOrCreate(externalIds: List, businessPartnerType: BusinessPartnerType): List { + val sharingStates = stateRepository.findByExternalIdInAndBusinessPartnerType(externalIds, businessPartnerType) + val sharingStatesByExternalId = sharingStates.associateBy { it.externalId } + + return externalIds.map { externalId -> + sharingStatesByExternalId[externalId] + ?: SharingState( + externalId, + businessPartnerType = businessPartnerType, + sharingStateType = SharingStateType.Initial, + sharingErrorCode = null, + sharingErrorMessage = null, + bpn = null, + sharingProcessStarted = null + ) + } + } + + private fun getOrCreate(externalId: String, businessPartnerType: BusinessPartnerType): SharingState { + return getOrCreate(listOf(externalId), businessPartnerType).single() } + + data class SharingStateIdentifierDto( + val externalId: String, + val businessPartnerType: BusinessPartnerType + ) + + data class PendingRequest( + val sharingStateId: SharingStateIdentifierDto, + val taskId: String, + val startTimeOverwrite: LocalDateTime? = null + ) + + data class SuccessRequest( + val sharingStateId: SharingStateIdentifierDto, + val bpn: String, + val startTimeOverwrite: LocalDateTime? = null + ) + + data class ErrorRequest( + val sharingStateId: SharingStateIdentifierDto, + val errorCode: BusinessPartnerSharingError, + val errorMessage: String?, + val startTimeOverwrite: LocalDateTime? = null + ) } \ No newline at end of file diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/SitePersistenceService.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/SitePersistenceService.kt index b0953a671..3f1571728 100644 --- a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/SitePersistenceService.kt +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/SitePersistenceService.kt @@ -24,7 +24,6 @@ import org.eclipse.tractusx.bpdm.common.exception.BpdmNotFoundException import org.eclipse.tractusx.bpdm.common.model.StageType import org.eclipse.tractusx.bpdm.common.util.replace import org.eclipse.tractusx.bpdm.gate.api.model.ChangelogType -import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateType import org.eclipse.tractusx.bpdm.gate.api.model.request.SiteGateInputRequest import org.eclipse.tractusx.bpdm.gate.api.model.request.SiteGateOutputRequest import org.eclipse.tractusx.bpdm.gate.entity.* @@ -73,6 +72,9 @@ class SitePersistenceService( sharingStateService.upsertSharingState(site.toSharingStateDTO()) } } + + val initRequests = sites.map { SharingStateService.SharingStateIdentifierDto(it.externalId, BusinessPartnerType.SITE ) } + sharingStateService.setInitial(initRequests) } //Creates Changelog For both Site and Logistic Address when they are created or updated @@ -149,8 +151,15 @@ class SitePersistenceService( saveChangelog(site.externalId, ChangelogType.CREATE, datatype) } } - sharingStateService.upsertSharingState(site.toSharingStateDTO(SharingStateType.Success)) } + + val successRequests = sites.map { + SharingStateService.SuccessRequest( + SharingStateService.SharingStateIdentifierDto(it.externalId, BusinessPartnerType.SITE), + it.bpn + ) + } + sharingStateService.setSuccess(successRequests) } private fun updateSiteOutput(site: Site, updatedSite: SiteGateOutputRequest, legalEntityRecord: LegalEntity) { diff --git a/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateControllerIT.kt b/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateControllerIT.kt index 0bc1cb13e..fb14ede2a 100644 --- a/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateControllerIT.kt +++ b/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateControllerIT.kt @@ -20,21 +20,25 @@ package org.eclipse.tractusx.bpdm.gate.controller import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy import org.eclipse.tractusx.bpdm.common.dto.BusinessPartnerType import org.eclipse.tractusx.bpdm.common.dto.request.PaginationRequest +import org.eclipse.tractusx.bpdm.common.dto.response.PageDto import org.eclipse.tractusx.bpdm.gate.api.client.GateClient -import org.eclipse.tractusx.bpdm.gate.api.exception.BusinessPartnerSharingError -import org.eclipse.tractusx.bpdm.gate.api.exception.BusinessPartnerSharingError.* +import org.eclipse.tractusx.bpdm.gate.api.exception.BusinessPartnerSharingError.SharingProcessError import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateType import org.eclipse.tractusx.bpdm.gate.api.model.response.SharingStateDto import org.eclipse.tractusx.bpdm.gate.util.DbTestHelpers import org.eclipse.tractusx.bpdm.gate.util.PostgreSQLContextInitializer import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource 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 +import org.springframework.web.reactive.function.client.WebClientResponseException import java.time.LocalDateTime @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @@ -51,152 +55,456 @@ class SharingStateControllerIT @Autowired constructor( testHelpers.truncateDbTables() } - @Test - fun `insert and get sharing states `() { + /** + * Sharing state in which all fields are filled + * Useful for streamlining tests + */ + val fullSharingState = SharingStateDto( + businessPartnerType = BusinessPartnerType.ADDRESS, + externalId = "exIdAddress", + sharingStateType = SharingStateType.Error, + sharingErrorCode = SharingProcessError, + sharingErrorMessage = "Message", + bpn = "TEST_BPN", + sharingProcessStarted = LocalDateTime.of(1900, 1, 1, 1, 1) + ) + + val sharingProcessStartedTime = LocalDateTime.of(2000, 10, 9, 8, 7) + + @ParameterizedTest + @EnumSource(BusinessPartnerType::class) + fun `insert minimal initial sharing state accepted`(bpType: BusinessPartnerType) { + val givenExternalId = "external-id" + + val givenSharingState = SharingStateDto(businessPartnerType = bpType, externalId = givenExternalId) + gateClient.sharingState.upsertSharingState(givenSharingState) + + val expected = PageDto(1, 1, 0, 1, listOf(givenSharingState)) + val actual = gateClient.sharingState.getSharingStates(PaginationRequest(0, 1), bpType, listOf(givenExternalId)) + + assertThat(actual).isEqualTo(expected) + } - val stateAddress = insertSharingStateSuccess(BusinessPartnerType.ADDRESS, externalId = "exIdAddress") - val stateSite = insertSharingStateSuccess(BusinessPartnerType.SITE, externalId = "exIdSite") - val stateLegalEntity1 = insertSharingStateSuccess(BusinessPartnerType.LEGAL_ENTITY, externalId = "exIdEntity1") - val stateLegalEntity2 = insertSharingStateSuccess(BusinessPartnerType.LEGAL_ENTITY, externalId = "exIdEntity2") - val stateGeneric = insertSharingStateSuccess(BusinessPartnerType.GENERIC, externalId = "exIdGeneric1") - insertSharingStateSuccess(BusinessPartnerType.ADDRESS, externalId = "exIdMultiple") - insertSharingStateSuccess(BusinessPartnerType.SITE, externalId = "exIdMultiple") - insertSharingStateSuccess(BusinessPartnerType.LEGAL_ENTITY, externalId = "exIdMultiple") - insertSharingStateSuccess(BusinessPartnerType.GENERIC, externalId = "exIdMultiple") + @ParameterizedTest + @EnumSource(BusinessPartnerType::class) + fun `insert initial sharing state with additional information ignored`(bpType: BusinessPartnerType) { + val externalId = "external-id" + val givenSharingState = fullSharingState.copy( + sharingStateType = SharingStateType.Initial, + businessPartnerType = bpType, + externalId = externalId + ) - val searchAddressById = readSharingStates(BusinessPartnerType.ADDRESS, "exIdAddress") - assertThat(searchAddressById).hasSize(1) - assertThat(searchAddressById.first()).isEqualTo(stateAddress) + gateClient.sharingState.upsertSharingState(givenSharingState) - val searchSitesById = readSharingStates(BusinessPartnerType.SITE, "exIdSite") - assertThat(searchSitesById).hasSize(1) - assertThat(searchSitesById.first()).isEqualTo(stateSite) + val expectedSharingState = SharingStateDto(businessPartnerType = bpType, externalId = externalId) + val expected = PageDto(1, 1, 0, 1, listOf(expectedSharingState)) + val actual = gateClient.sharingState.getSharingStates(PaginationRequest(0, 1), bpType, listOf(externalId)) - val searchAddressWrongId = readSharingStates(BusinessPartnerType.ADDRESS, "exIdEntity") - assertThat(searchAddressWrongId).hasSize(0) + assertThat(actual).isEqualTo(expected) + } - val searchEntityMultiple = readSharingStates(BusinessPartnerType.LEGAL_ENTITY, "exIdEntity1", "exIdEntity2") - assertThat(searchEntityMultiple).hasSize(2) + @ParameterizedTest + @EnumSource(BusinessPartnerType::class) + fun `insert initial sharing state resets time`(bpType: BusinessPartnerType) { + val externalId = "external-id" + val givenBpn = "TEST_BPN" - val searchEntitySingle = readSharingStates(BusinessPartnerType.LEGAL_ENTITY, "exIdEntity2") - assertThat(searchEntitySingle).hasSize(1) - assertThat(searchEntitySingle.first()).isEqualTo(stateLegalEntity2) + val initialState = SharingStateDto( + sharingStateType = SharingStateType.Initial, + businessPartnerType = bpType, + externalId = externalId + ) - val searchGenericSingle = readSharingStates(BusinessPartnerType.GENERIC, "exIdGeneric1") - assertThat(searchGenericSingle).hasSize(1) - assertThat(searchGenericSingle.first()).isEqualTo(stateGeneric) + val successState = initialState.copy( + sharingStateType = SharingStateType.Success, + bpn = givenBpn, + sharingProcessStarted = LocalDateTime.now() + ) - val searchAll = readSharingStates(null) - assertThat(searchAll).hasSize(9) + gateClient.sharingState.upsertSharingState(successState) + gateClient.sharingState.upsertSharingState(initialState) - val searchEntityAllLegalEntities = readSharingStates(BusinessPartnerType.LEGAL_ENTITY) - assertThat(searchEntityAllLegalEntities).hasSize(3) - assertThat(searchEntityAllLegalEntities).extracting(SharingStateDto::externalId.name) - .contains(stateLegalEntity1.externalId, stateLegalEntity2.externalId, "exIdMultiple") + val expectedSharingState = initialState.copy(bpn = givenBpn) + val expected = PageDto(1, 1, 0, 1, listOf(expectedSharingState)) + val actual = gateClient.sharingState.getSharingStates(PaginationRequest(0, 1), bpType, listOf(externalId)) - val searchAllWithSameId = readSharingStates(null, "exIdMultiple") - assertThat(searchAllWithSameId).hasSize(4) - assertThat(searchAllWithSameId).extracting(SharingStateDto::externalId.name) - .containsOnly("exIdMultiple") + assertThat(actual).isEqualTo(expected) + } + + + @ParameterizedTest + @EnumSource(BusinessPartnerType::class) + fun `insert minimal pending sharing state accepted`(bpType: BusinessPartnerType) { + val givenBeforeInsert = LocalDateTime.now() + val givenExternalId = "external-id" + val givenSharingState = SharingStateDto(sharingStateType = SharingStateType.Pending, businessPartnerType = bpType, externalId = givenExternalId) + gateClient.sharingState.upsertSharingState(givenSharingState) + + val expected = PageDto(1, 1, 0, 1, listOf(givenSharingState)) + val actual = gateClient.sharingState.getSharingStates(PaginationRequest(0, 1), bpType, listOf(givenExternalId)) + + val afterInsert = LocalDateTime.now() + assertThat(actual) + .usingRecursiveComparison() + .ignoringFieldsMatchingRegexes(".*${SharingStateDto::sharingProcessStarted.name}") + .isEqualTo(expected) + + assertThat(actual.content.single().sharingProcessStarted).isBetween(givenBeforeInsert, afterInsert) } - @Test - fun `insert and get sharing states with error code`() { + @ParameterizedTest + @EnumSource(BusinessPartnerType::class) + fun `insert pending sharing state with sharingProcessStart accepted`(bpType: BusinessPartnerType) { + val givenExternalId = "external-id" + val givenSharingProcessStart = sharingProcessStartedTime + + val givenSharingState = SharingStateDto( + sharingStateType = SharingStateType.Pending, + businessPartnerType = bpType, + externalId = givenExternalId, + sharingProcessStarted = givenSharingProcessStart + ) + gateClient.sharingState.upsertSharingState(givenSharingState) - val stateAddress1 = insertSharingStateError(BusinessPartnerType.ADDRESS, externalId = "exIdAddress1", errorCode = SharingTimeout) - insertSharingStateError(BusinessPartnerType.ADDRESS, externalId = "exIdAddress2", errorCode = SharingProcessError) - insertSharingStateError(BusinessPartnerType.ADDRESS, externalId = "exIdAddress3", errorCode = BpnNotInPool) + val expected = PageDto(1, 1, 0, 1, listOf(givenSharingState)) + val actual = gateClient.sharingState.getSharingStates(PaginationRequest(0, 1), bpType, listOf(givenExternalId)) - val searchAddress = readSharingStates(BusinessPartnerType.ADDRESS, "exIdAddress1") - assertThat(searchAddress).hasSize(1) - assertThat(searchAddress.first()).isEqualTo(stateAddress1) + assertThat(actual).isEqualTo(expected) } + @ParameterizedTest + @EnumSource(BusinessPartnerType::class) + fun `insert pending sharing state with additional fields ignored`(bpType: BusinessPartnerType) { + val givenExternalId = "external-id" + val givenSharingProcessStart = sharingProcessStartedTime + + val givenSharingState = fullSharingState.copy( + sharingStateType = SharingStateType.Pending, + businessPartnerType = bpType, + externalId = givenExternalId, + sharingProcessStarted = givenSharingProcessStart + ) + gateClient.sharingState.upsertSharingState(givenSharingState) - @Test - fun `insert and update states`() { + val expectedSharingState = SharingStateDto( + sharingStateType = SharingStateType.Pending, + businessPartnerType = bpType, + externalId = givenExternalId, + sharingProcessStarted = givenSharingProcessStart + ) + val expected = PageDto(1, 1, 0, 1, listOf(expectedSharingState)) + val actual = gateClient.sharingState.getSharingStates(PaginationRequest(0, 1), bpType, listOf(givenExternalId)) - val stateAddress1 = insertSharingStateError(BusinessPartnerType.ADDRESS, externalId = "exIdAddress1", errorCode = SharingTimeout) - insertSharingStateError(BusinessPartnerType.ADDRESS, externalId = "exIdAddress2", errorCode = SharingProcessError) - insertSharingStateError(BusinessPartnerType.ADDRESS, externalId = "exIdAddress3", errorCode = BpnNotInPool) + assertThat(actual).isEqualTo(expected) + } - val searchAddress = readSharingStates(BusinessPartnerType.ADDRESS, "exIdAddress1") - assertThat(searchAddress).hasSize(1) - assertThat(searchAddress.first()).isEqualTo(stateAddress1) + @ParameterizedTest + @EnumSource(BusinessPartnerType::class) + fun `insert minimal success sharing state accepted`(bpType: BusinessPartnerType) { + val givenExternalId = "external-id" + val givenBpn = "BPN_TEST" + val beforeInsert = sharingProcessStartedTime - val updatedAddress1 = stateAddress1.copy( + val givenSharingState = SharingStateDto( sharingStateType = SharingStateType.Success, - sharingErrorCode = BpnNotInPool, - sharingProcessStarted = LocalDateTime.now().withNano(0), - sharingErrorMessage = "Changed ", - bpn = null + businessPartnerType = bpType, + externalId = givenExternalId, + bpn = givenBpn ) + gateClient.sharingState.upsertSharingState(givenSharingState) + - gateClient.sharingState.upsertSharingState(updatedAddress1) + val expected = PageDto(1, 1, 0, 1, listOf(givenSharingState)) + val actual = gateClient.sharingState.getSharingStates(PaginationRequest(0, 1), bpType, listOf(givenExternalId)) - val readUpdatedAddress = readSharingStates(BusinessPartnerType.ADDRESS, "exIdAddress1") - assertThat(readUpdatedAddress).hasSize(1) - assertThat(readUpdatedAddress.first()).isEqualTo(updatedAddress1) + val afterInsert = LocalDateTime.now() + + assertThat(actual) + .usingRecursiveComparison() + .ignoringFieldsMatchingRegexes(".*${SharingStateDto::sharingProcessStarted.name}") + .isEqualTo(expected) + + assertThat(actual.content.single().sharingProcessStarted).isBetween(beforeInsert, afterInsert) } - @Test - fun `insert and update states with sharingProcessStarted`() { + @ParameterizedTest + @EnumSource(BusinessPartnerType::class) + fun `insert success sharing state with sharingProcessStart accepted`(bpType: BusinessPartnerType) { + val givenExternalId = "external-id" + val givenSharingProcessStart = sharingProcessStartedTime + val givenBpn = "TEST_BPN" - val startTime = LocalDateTime.now().withNano(0) - val stateAddress1 = insertSharingStateSuccess( - businessPartnerType = BusinessPartnerType.ADDRESS, externalId = "exIdAddress1", - sharingProcessStarted = startTime + val givenSharingState = SharingStateDto( + sharingStateType = SharingStateType.Success, + businessPartnerType = bpType, + externalId = givenExternalId, + sharingProcessStarted = givenSharingProcessStart, + bpn = givenBpn ) + gateClient.sharingState.upsertSharingState(givenSharingState) + + val expected = PageDto(1, 1, 0, 1, listOf(givenSharingState)) + val actual = gateClient.sharingState.getSharingStates(PaginationRequest(0, 1), bpType, listOf(givenExternalId)) - val readInsertedAddress = readSharingStates(BusinessPartnerType.ADDRESS, "exIdAddress1") - assertThat(readInsertedAddress.first().sharingProcessStarted).isEqualTo(startTime) + assertThat(actual).isEqualTo(expected) + } + + @ParameterizedTest + @EnumSource(BusinessPartnerType::class) + fun `insert success sharing state with additional properties ignored`(bpType: BusinessPartnerType) { + val givenExternalId = "external-id" + val givenSharingProcessStart = sharingProcessStartedTime + val givenBpn = "TEST_BPN" + + val givenSharingState = fullSharingState.copy( + sharingStateType = SharingStateType.Success, + businessPartnerType = bpType, + externalId = givenExternalId, + sharingProcessStarted = givenSharingProcessStart, + bpn = givenBpn + ) + gateClient.sharingState.upsertSharingState(givenSharingState) + + val expectedSharingState = SharingStateDto( + sharingStateType = SharingStateType.Success, + businessPartnerType = bpType, + externalId = givenExternalId, + sharingProcessStarted = givenSharingProcessStart, + bpn = givenBpn + ) + val expected = PageDto(1, 1, 0, 1, listOf(expectedSharingState)) + val actual = gateClient.sharingState.getSharingStates(PaginationRequest(0, 1), bpType, listOf(givenExternalId)) + + assertThat(actual).isEqualTo(expected) + } - val updatedWithEmpyStarted = stateAddress1.copy( + @ParameterizedTest + @EnumSource(BusinessPartnerType::class) + fun `insert success sharing state without BPN throws exception`(bpType: BusinessPartnerType) { + val givenExternalId = "external-id" + + val sharingState = SharingStateDto( + sharingStateType = SharingStateType.Success, + businessPartnerType = bpType, + externalId = givenExternalId + ) + + assertThatThrownBy { + gateClient.sharingState.upsertSharingState(sharingState) + }.isInstanceOf(WebClientResponseException::class.java) + + } + + @ParameterizedTest + @EnumSource(BusinessPartnerType::class) + fun `insert minimal error sharing state accepted`(bpType: BusinessPartnerType) { + val givenExternalId = "external-id" + val beforeInsert = LocalDateTime.now() + + val givenSharingState = SharingStateDto( sharingStateType = SharingStateType.Error, - sharingProcessStarted = null, - sharingErrorMessage = "Changed", + businessPartnerType = bpType, + externalId = givenExternalId, + sharingErrorCode = SharingProcessError ) - gateClient.sharingState.upsertSharingState(updatedWithEmpyStarted) + gateClient.sharingState.upsertSharingState(givenSharingState) + - val readUpdatedAddress = readSharingStates(BusinessPartnerType.ADDRESS, "exIdAddress1") - assertThat(readUpdatedAddress.first().sharingStateType).isEqualTo(SharingStateType.Error) - assertThat(readUpdatedAddress.first().sharingProcessStarted).isEqualTo(startTime).describedAs("Update with null - sharingProcessStarted not changed ") - assertThat(readUpdatedAddress.first().sharingErrorMessage).isEqualTo("Changed") + val expected = PageDto(1, 1, 0, 1, listOf(givenSharingState)) + val actual = gateClient.sharingState.getSharingStates(PaginationRequest(0, 1), bpType, listOf(givenExternalId)) + val afterInsert = LocalDateTime.now() + + assertThat(actual) + .usingRecursiveComparison() + .ignoringFieldsMatchingRegexes(".*${SharingStateDto::sharingProcessStarted.name}") + .isEqualTo(expected) + + assertThat(actual.content.single().sharingProcessStarted).isBetween(beforeInsert, afterInsert) } - /** - * Insert Sharing State only with required fields filled - */ - fun insertSharingStateSuccess(businessPartnerType: BusinessPartnerType, externalId: String, sharingProcessStarted: LocalDateTime? = null): SharingStateDto { + @ParameterizedTest + @EnumSource(BusinessPartnerType::class) + fun `insert error sharing state with sharingProcessStart accepted`(bpType: BusinessPartnerType) { + val givenExternalId = "external-id" + val givenSharingProcessStart = sharingProcessStartedTime - val newState = SharingStateDto( - businessPartnerType = businessPartnerType, - externalId = externalId, + val givenSharingState = SharingStateDto( + sharingStateType = SharingStateType.Error, + businessPartnerType = bpType, + externalId = givenExternalId, + sharingProcessStarted = givenSharingProcessStart, + sharingErrorCode = SharingProcessError + ) + gateClient.sharingState.upsertSharingState(givenSharingState) + + val expected = PageDto(1, 1, 0, 1, listOf(givenSharingState)) + val actual = gateClient.sharingState.getSharingStates(PaginationRequest(0, 1), bpType, listOf(givenExternalId)) + + assertThat(actual).isEqualTo(expected) + } + + @ParameterizedTest + @EnumSource(BusinessPartnerType::class) + fun `insert error sharing state with additional properties ignored`(bpType: BusinessPartnerType) { + val givenExternalId = "external-id" + val givenSharingProcessStart = sharingProcessStartedTime + val givenBpn = "TEST_BPN" + val givenErrorMessage = "test message" + + val givenSharingState = SharingStateDto( + sharingStateType = SharingStateType.Error, + businessPartnerType = bpType, + externalId = givenExternalId, + sharingProcessStarted = givenSharingProcessStart, + sharingErrorCode = SharingProcessError, + sharingErrorMessage = givenErrorMessage, + bpn = givenBpn + ) + gateClient.sharingState.upsertSharingState(givenSharingState) + + val expectedSharingState = givenSharingState.copy(bpn = null) + val expected = PageDto(1, 1, 0, 1, listOf(expectedSharingState)) + val actual = gateClient.sharingState.getSharingStates(PaginationRequest(0, 1), bpType, listOf(givenExternalId)) + + assertThat(actual).isEqualTo(expected) + } + + @ParameterizedTest + @EnumSource(BusinessPartnerType::class) + fun `insert error sharing state without error code throws exception`(bpType: BusinessPartnerType) { + val givenExternalId = "external-id" + + val sharingState = SharingStateDto( + sharingStateType = SharingStateType.Error, + businessPartnerType = bpType, + externalId = givenExternalId + ) + + assertThatThrownBy { + gateClient.sharingState.upsertSharingState(sharingState) + }.isInstanceOf(WebClientResponseException::class.java) + + } + + @ParameterizedTest + @EnumSource(BusinessPartnerType::class) + fun `keep BPN through sharing state lifecycle`(bpType: BusinessPartnerType) { + val givenExternalId = "external-id" + val givenBpn = "TEST_BPN" + val givenSharingProcessStart = sharingProcessStartedTime + + val successState = SharingStateDto( + businessPartnerType = bpType, + externalId = givenExternalId, sharingStateType = SharingStateType.Success, - sharingErrorCode = null, - sharingProcessStarted = sharingProcessStarted, - sharingErrorMessage = null, + bpn = givenBpn + ) + + val initialState = successState.copy( + sharingStateType = SharingStateType.Initial, bpn = null ) - gateClient.sharingState.upsertSharingState(newState) - return newState + + val pendingState = successState.copy( + sharingStateType = SharingStateType.Pending, + sharingProcessStarted = givenSharingProcessStart, + bpn = null + ) + + val errorState = successState.copy( + businessPartnerType = bpType, + externalId = givenExternalId, + sharingStateType = SharingStateType.Error, + sharingErrorCode = SharingProcessError, + bpn = null + ) + + gateClient.sharingState.upsertSharingState(successState) + gateClient.sharingState.upsertSharingState(initialState) + gateClient.sharingState.upsertSharingState(pendingState) + gateClient.sharingState.upsertSharingState(errorState) + + val actual = gateClient.sharingState.getSharingStates( + businessPartnerType = bpType, + externalIds = listOf(givenExternalId), + paginationRequest = PaginationRequest() + ) + + val expectedSharingState = errorState.copy(bpn = givenBpn, sharingProcessStarted = givenSharingProcessStart) + val expected = PageDto(1, 1, 0, 1, content = listOf(expectedSharingState)) + + assertThat(actual).isEqualTo(expected) + } + + @Test + fun `insert and get sharing states `() { + val stateAddress = insertSharingStateInitial(BusinessPartnerType.ADDRESS, externalId = "exIdAddress") + val stateSite = insertSharingStateInitial(BusinessPartnerType.SITE, externalId = "exIdSite") + val stateLegalEntity1 = insertSharingStateInitial(BusinessPartnerType.LEGAL_ENTITY, externalId = "exIdEntity1") + val stateLegalEntity2 = insertSharingStateInitial(BusinessPartnerType.LEGAL_ENTITY, externalId = "exIdEntity2") + val stateGeneric = insertSharingStateInitial(BusinessPartnerType.GENERIC, externalId = "exIdGeneric1") + insertSharingStateInitial(BusinessPartnerType.ADDRESS, externalId = "exIdMultiple") + insertSharingStateInitial(BusinessPartnerType.SITE, externalId = "exIdMultiple") + insertSharingStateInitial(BusinessPartnerType.LEGAL_ENTITY, externalId = "exIdMultiple") + insertSharingStateInitial(BusinessPartnerType.GENERIC, externalId = "exIdMultiple") + + + val searchAddressById = readSharingStates(BusinessPartnerType.ADDRESS, "exIdAddress") + assertThat(searchAddressById).hasSize(1) + assertThat(searchAddressById.single()).isEqualTo(stateAddress) + + val searchSitesById = readSharingStates(BusinessPartnerType.SITE, "exIdSite") + assertThat(searchSitesById).hasSize(1) + assertThat(searchSitesById.single()).isEqualTo(stateSite) + + val searchAddressWrongId = readSharingStates(BusinessPartnerType.ADDRESS, "exIdEntity") + assertThat(searchAddressWrongId).hasSize(0) + + val searchEntityMultiple = readSharingStates(BusinessPartnerType.LEGAL_ENTITY, "exIdEntity1", "exIdEntity2") + assertThat(searchEntityMultiple).hasSize(2) + + val searchEntitySingle = readSharingStates(BusinessPartnerType.LEGAL_ENTITY, "exIdEntity2") + assertThat(searchEntitySingle).hasSize(1) + assertThat(searchEntitySingle.single()).isEqualTo(stateLegalEntity2) + + val searchGenericSingle = readSharingStates(BusinessPartnerType.GENERIC, "exIdGeneric1") + assertThat(searchGenericSingle).hasSize(1) + assertThat(searchGenericSingle.first()).isEqualTo(stateGeneric) + + val searchAll = readSharingStates(null) + assertThat(searchAll).hasSize(9) + + val searchEntityAllLegalEntities = readSharingStates(BusinessPartnerType.LEGAL_ENTITY) + assertThat(searchEntityAllLegalEntities).hasSize(3) + assertThat(searchEntityAllLegalEntities).extracting(SharingStateDto::externalId.name) + .contains(stateLegalEntity1.externalId, stateLegalEntity2.externalId, "exIdMultiple") + + val searchAllWithSameId = readSharingStates(null, "exIdMultiple") + assertThat(searchAllWithSameId).hasSize(4) + assertThat(searchAllWithSameId).extracting(SharingStateDto::externalId.name) + .containsOnly("exIdMultiple") + } /** - * Insert Sharing State with all Fields Field + * Insert Sharing State only with required fields filled */ - fun insertSharingStateError(businessPartnerType: BusinessPartnerType, externalId: String, errorCode: BusinessPartnerSharingError): SharingStateDto { + fun insertSharingStateInitial( + businessPartnerType: BusinessPartnerType, + externalId: String + ): SharingStateDto { val newState = SharingStateDto( businessPartnerType = businessPartnerType, externalId = externalId, - sharingStateType = SharingStateType.Error, - sharingErrorCode = errorCode, - sharingProcessStarted = LocalDateTime.now().withNano(0), - sharingErrorMessage = "Error in $businessPartnerType with external id $externalId", - bpn = "BPN" + externalId + sharingStateType = SharingStateType.Initial, + sharingErrorCode = null, + sharingProcessStarted = null, + sharingErrorMessage = null, + bpn = null ) gateClient.sharingState.upsertSharingState(newState) return newState @@ -207,5 +515,4 @@ class SharingStateControllerIT @Autowired constructor( return gateClient.sharingState.getSharingStates(PaginationRequest(), businessPartnerType, externalIds.asList()).content } - } \ No newline at end of file