From d72aca143e56cf15600e87f7b08fbd33702c3dd8 Mon Sep 17 00:00:00 2001 From: Martin Kaeser Date: Thu, 11 May 2023 14:32:36 +0200 Subject: [PATCH 1/8] feat(bridge): Update Gate through Sharing State API --- .../bpdm/bridge/dummy/service/SyncService.kt | 146 ++++++++++++++---- 1 file changed, 113 insertions(+), 33 deletions(-) diff --git a/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/service/SyncService.kt b/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/service/SyncService.kt index e6b4b4f60..148062013 100644 --- a/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/service/SyncService.kt +++ b/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/service/SyncService.kt @@ -22,9 +22,8 @@ package com.catenax.bpdm.bridge.dummy.service import mu.KotlinLogging import org.eclipse.tractusx.bpdm.common.dto.request.PaginationRequest import org.eclipse.tractusx.bpdm.gate.api.client.GateClient -import org.eclipse.tractusx.bpdm.gate.api.model.AddressGateInputResponse -import org.eclipse.tractusx.bpdm.gate.api.model.LegalEntityGateInputResponse -import org.eclipse.tractusx.bpdm.gate.api.model.SiteGateInputResponse +import org.eclipse.tractusx.bpdm.gate.api.exception.BusinessPartnerSharingError +import org.eclipse.tractusx.bpdm.gate.api.model.* import org.eclipse.tractusx.bpdm.gate.api.model.request.PaginationStartAfterRequest import org.eclipse.tractusx.bpdm.gate.api.model.response.LsaType import org.eclipse.tractusx.bpdm.pool.api.client.PoolApiClient @@ -32,6 +31,7 @@ import org.eclipse.tractusx.bpdm.pool.api.model.request.* import org.eclipse.tractusx.bpdm.pool.api.model.response.* import org.springframework.stereotype.Service import java.time.Instant +import java.time.LocalDateTime @Service class SyncService( @@ -162,8 +162,9 @@ class SyncService( } val responseWrapper = poolClient.legalEntities().createBusinessPartners(createRequests) - logger.info { "Pool accepted ${responseWrapper.entityCount} new legal entities, but ${responseWrapper.errorCount} were refused" } - handleLegalEntityCreateResponse(entriesToCreate, responseWrapper) + logger.info { "Pool accepted ${responseWrapper.entityCount} new legal entities, ${responseWrapper.errorCount} were refused" } + + handleLegalEntityCreateResponse(responseWrapper) } private fun updateLegalEntitiesInPool(entriesToUpdate: Collection) { @@ -175,8 +176,10 @@ class SyncService( } val responseWrapper = poolClient.legalEntities().updateBusinessPartners(updateRequests) - logger.info { "Pool accepted ${responseWrapper.entityCount} updated legal entities, but ${responseWrapper.errorCount} were refused" } - handleLegalEntityUpdateResponse(entriesToUpdate, responseWrapper) + logger.info { "Pool accepted ${responseWrapper.entityCount} updated legal entities, ${responseWrapper.errorCount} were refused" } + + val externalIdByBpn = entriesToUpdate.associateBy { it.bpn!! }.mapValues { (_, entry) -> entry.externalId } + handleLegalEntityUpdateResponse(responseWrapper, externalIdByBpn) } private fun createSitesInPool(entriesToCreate: List) { @@ -202,8 +205,9 @@ class SyncService( } } val responseWrapper = poolClient.sites().createSite(createRequests) - logger.info { "Pool accepted ${responseWrapper.entityCount} new sites, but ${responseWrapper.errorCount} were refused" } - handleSiteCreateResponse(entriesToCreate, responseWrapper) + logger.info { "Pool accepted ${responseWrapper.entityCount} new sites, ${responseWrapper.errorCount} were refused" } + + handleSiteCreateResponse(responseWrapper) } private fun updateSitesInPool(entriesToUpdate: List) { @@ -215,8 +219,10 @@ class SyncService( } val responseWrapper = poolClient.sites().updateSite(updateRequests) - logger.info { "Pool accepted ${responseWrapper.entityCount} updated sites, but ${responseWrapper.errorCount} were refused" } - handleSiteUpdateResponse(entriesToUpdate, responseWrapper) + logger.info { "Pool accepted ${responseWrapper.entityCount} updated sites, ${responseWrapper.errorCount} were refused" } + + val externalIdByBpn = entriesToUpdate.associateBy { it.bpn!! }.mapValues { (_, entry) -> entry.externalId } + handleSiteUpdateResponse(responseWrapper, externalIdByBpn) } private fun createAddressesInPool(entriesToCreate: List) { @@ -258,8 +264,9 @@ class SyncService( } } val responseWrapper = poolClient.addresses().createAddresses(createRequests) - logger.info { "Pool accepted ${responseWrapper.entityCount} new addresses, but ${responseWrapper.errorCount} were refused" } - handleAddressCreateResponse(entriesToCreate, responseWrapper) + logger.info { "Pool accepted ${responseWrapper.entityCount} new addresses, ${responseWrapper.errorCount} were refused" } + + handleAddressCreateResponse(responseWrapper) } private fun updateAddressesInPool(entriesToUpdate: List) { @@ -271,8 +278,10 @@ class SyncService( } val responseWrapper = poolClient.addresses().updateAddresses(updateRequests) - logger.info { "Pool accepted ${responseWrapper.entityCount} updated addresses, but ${responseWrapper.errorCount} were refused" } - handleAddressUpdateResponse(entriesToUpdate, responseWrapper) + logger.info { "Pool accepted ${responseWrapper.entityCount} updated addresses, ${responseWrapper.errorCount} were refused" } + + val externalIdByBpn = entriesToUpdate.associateBy { it.bpn!! }.mapValues { (_, entry) -> entry.externalId } + handleAddressUpdateResponse(responseWrapper, externalIdByBpn) } @@ -282,44 +291,115 @@ class SyncService( // For improved robustness we should maybe persistently track all sync entries (status) by LSAType/externalID. private fun handleLegalEntityCreateResponse( - legalEntitiesFromGate: Collection, - createResponseWrapper: LegalEntityPartnerCreateResponseWrapper + responseWrapper: LegalEntityPartnerCreateResponseWrapper ) { - logger.info { "TODO: BPNLs for ${createResponseWrapper.entityCount} new legal entities should be updated in the Gate" } + for (entity in responseWrapper.entities) { + buildSuccessSharingStateDto(LsaType.LegalEntity, entity.index, entity.legalEntity.bpn) + ?.let { gateClient.sharingState().upsertSharingState(it) } + } + for (errorInfo in responseWrapper.errors) { + // entityKey should be an externalId + buildErrorSharingStateDto(LsaType.LegalEntity, errorInfo.entityKey, errorInfo, true) + ?.let { gateClient.sharingState().upsertSharingState(it) } + } + logger.info { "Sharing states for ${responseWrapper.entityCount} valid and ${responseWrapper.errorCount} invalid new legal entities were updated in the Gate" } } private fun handleLegalEntityUpdateResponse( - legalEntitiesFromGate: Collection, - updateResponseWrapper: LegalEntityPartnerUpdateResponseWrapper + responseWrapper: LegalEntityPartnerUpdateResponseWrapper, + externalIdByBpn: Map ) { - logger.info { "TODO: BPNLs for ${updateResponseWrapper.entityCount} legal entities should be updated in the Gate" } + for (errorInfo in responseWrapper.errors) { + // entityKey should be a BPN + val externalId = externalIdByBpn[errorInfo.entityKey] + buildErrorSharingStateDto(LsaType.LegalEntity, externalId, errorInfo, false) + ?.let { gateClient.sharingState().upsertSharingState(it) } + } + logger.info { "Sharing states for ${responseWrapper.errorCount} invalid modified legal entities were updated in the Gate" } } private fun handleSiteCreateResponse( - sitesFromGate: Collection, - createResponseWrapper: SitePartnerCreateResponseWrapper + responseWrapper: SitePartnerCreateResponseWrapper ) { - logger.info { "TODO: BPNLs for ${createResponseWrapper.entityCount} new sites should be updated in the Gate" } + for (entity in responseWrapper.entities) { + buildSuccessSharingStateDto(LsaType.Site, entity.index, entity.site.bpn) + ?.let { gateClient.sharingState().upsertSharingState(it) } + } + for (errorInfo in responseWrapper.errors) { + // entityKey should be an externalId + buildErrorSharingStateDto(LsaType.Site, errorInfo.entityKey, errorInfo, true) + ?.let { gateClient.sharingState().upsertSharingState(it) } + } + logger.info { "Sharing states for ${responseWrapper.entityCount} valid and ${responseWrapper.errorCount} invalid new sites were updated in the Gate" } } private fun handleSiteUpdateResponse( - sitesFromGate: Collection, - updateResponseWrapper: SitePartnerUpdateResponseWrapper + responseWrapper: SitePartnerUpdateResponseWrapper, + externalIdByBpn: Map ) { - logger.info { "TODO: BPNLs for ${updateResponseWrapper.entityCount} sites should be updated in the Gate" } + for (errorInfo in responseWrapper.errors) { + // entityKey should be a BPN + val externalId = externalIdByBpn[errorInfo.entityKey] + buildErrorSharingStateDto(LsaType.Site, externalId, errorInfo, false) + ?.let { gateClient.sharingState().upsertSharingState(it) } + } + logger.info { "Sharing states for ${responseWrapper.errorCount} invalid modified sites were updated in the Gate" } } private fun handleAddressCreateResponse( - addressesFromGate: Collection, - createResponseWrapper: AddressPartnerCreateResponseWrapper + responseWrapper: AddressPartnerCreateResponseWrapper ) { - logger.info { "TODO: BPNLs for ${createResponseWrapper.entityCount} new addresses should be updated in the Gate" } + for (entity in responseWrapper.entities) { + buildSuccessSharingStateDto(LsaType.Address, entity.index, entity.address.bpn) + ?.let { gateClient.sharingState().upsertSharingState(it) } + } + for (errorInfo in responseWrapper.errors) { + // entityKey should be an externalId + buildErrorSharingStateDto(LsaType.Address, errorInfo.entityKey, errorInfo, true) + ?.let { gateClient.sharingState().upsertSharingState(it) } + } + logger.info { "Sharing states for ${responseWrapper.entityCount} valid and ${responseWrapper.errorCount} invalid new addresses were updated in the Gate" } } private fun handleAddressUpdateResponse( - addressesFromGate: Collection, - updateResponseWrapper: AddressPartnerUpdateResponseWrapper + responseWrapper: AddressPartnerUpdateResponseWrapper, + externalIdByBpn: Map ) { - logger.info { "TODO: BPNLs for ${updateResponseWrapper.entityCount} addresses should be updated in the Gate" } + for (errorInfo in responseWrapper.errors) { + // entityKey should be a BPN + val externalId = externalIdByBpn[errorInfo.entityKey] + buildErrorSharingStateDto(LsaType.Address, externalId, errorInfo, false) + ?.let { gateClient.sharingState().upsertSharingState(it) } + } + logger.info { "Sharing states for ${responseWrapper.errorCount} invalid modified addresses were updated in the Gate" } + } + + private fun buildSuccessSharingStateDto(lsaType: LsaType, index: String?, bpn: String): SharingStateDto? { + if (index == null) { + logger.warn { "Encountered index=null in Pool response for $bpn, can't update the Gate sharing state" } + return null + } + return SharingStateDto( + lsaType = lsaType, + externalId = index, + sharingStateType = SharingStateType.Success, + bpn = bpn, + sharingProcessStarted = LocalDateTime.now() + ) + } + + private fun buildErrorSharingStateDto(lsaType: LsaType, externalId: String?, errorInfo: ErrorInfo<*>, processStarted: Boolean): SharingStateDto? { + if (externalId == null) { + logger.warn { "Couldn't determine externalId for $errorInfo, can't update the Gate sharing state" } + return null + } + return SharingStateDto( + lsaType = lsaType, + externalId = externalId, + sharingStateType = SharingStateType.Error, + sharingErrorCode = BusinessPartnerSharingError.SharingProcessError, + sharingErrorMessage = "${errorInfo.message} (${errorInfo.errorCode})", + sharingProcessStarted = if (processStarted) LocalDateTime.now() else null + ) } } \ No newline at end of file From 4077f9200b673675e546a2ca5494c97eff5bfd5e Mon Sep 17 00:00:00 2001 From: Martin Kaeser Date: Thu, 11 May 2023 18:41:56 +0200 Subject: [PATCH 2/8] feat(bridge): Get BPN through Gate's Sharing State API --- .../bpdm/bridge/dummy/dto/GateAddressInfo.kt | 30 +++++ .../bridge/dummy/dto/GateLegalEntityInfo.kt | 28 +++++ .../bpdm/bridge/dummy/dto/GateSiteInfo.kt | 29 +++++ .../bpdm/bridge/dummy/service/SyncService.kt | 116 +++++++++++++----- 4 files changed, 175 insertions(+), 28 deletions(-) create mode 100644 bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/dto/GateAddressInfo.kt create mode 100644 bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/dto/GateLegalEntityInfo.kt create mode 100644 bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/dto/GateSiteInfo.kt diff --git a/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/dto/GateAddressInfo.kt b/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/dto/GateAddressInfo.kt new file mode 100644 index 000000000..479fc02c5 --- /dev/null +++ b/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/dto/GateAddressInfo.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 com.catenax.bpdm.bridge.dummy.dto + +import org.eclipse.tractusx.bpdm.common.dto.LogisticAddressDto + +data class GateAddressInfo( + val address: LogisticAddressDto, + val externalId: String, + val legalEntityExternalId: String?, + val siteExternalId: String?, + val bpn: String? +) diff --git a/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/dto/GateLegalEntityInfo.kt b/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/dto/GateLegalEntityInfo.kt new file mode 100644 index 000000000..13c52d44c --- /dev/null +++ b/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/dto/GateLegalEntityInfo.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 com.catenax.bpdm.bridge.dummy.dto + +import org.eclipse.tractusx.bpdm.common.dto.LegalEntityDto + +data class GateLegalEntityInfo( + val legalEntity: LegalEntityDto, + val externalId: String, + val bpn: String? +) diff --git a/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/dto/GateSiteInfo.kt b/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/dto/GateSiteInfo.kt new file mode 100644 index 000000000..165c27565 --- /dev/null +++ b/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/dto/GateSiteInfo.kt @@ -0,0 +1,29 @@ +/******************************************************************************* + * 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 com.catenax.bpdm.bridge.dummy.dto + +import org.eclipse.tractusx.bpdm.common.dto.SiteDto + +data class GateSiteInfo( + val site: SiteDto, + val externalId: String, + val legalEntityExternalId: String, + val bpn: String? +) diff --git a/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/service/SyncService.kt b/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/service/SyncService.kt index 148062013..84950445c 100644 --- a/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/service/SyncService.kt +++ b/bpdm-bridge-dummy/src/main/kotlin/com/catenax/bpdm/bridge/dummy/service/SyncService.kt @@ -19,6 +19,9 @@ package com.catenax.bpdm.bridge.dummy.service +import com.catenax.bpdm.bridge.dummy.dto.GateAddressInfo +import com.catenax.bpdm.bridge.dummy.dto.GateLegalEntityInfo +import com.catenax.bpdm.bridge.dummy.dto.GateSiteInfo import mu.KotlinLogging import org.eclipse.tractusx.bpdm.common.dto.request.PaginationRequest import org.eclipse.tractusx.bpdm.gate.api.client.GateClient @@ -51,7 +54,7 @@ class SyncService( externalIdsByType[LsaType.Address]?.let { syncAddresses(it) } } - private fun getChangedExternalIdsByLsaTypeFromGate(modifiedAfter: Instant?): Map> { + private fun getChangedExternalIdsByLsaTypeFromGate(modifiedAfter: Instant?): Map> { // TODO use pagination properly val entriesGate = gateClient.changelog().getChangelogEntriesLsaType( lsaType = null, @@ -72,9 +75,9 @@ class SyncService( } } - private fun syncLegalEntities(externalIdsRequested: Collection) { + private fun syncLegalEntities(externalIdsRequested: Set) { // Retrieve business partners (LSA) from Gate - val entries = getLegalEntitiesFromGate(externalIdsRequested) + val entries = getLegalEntityInfosFromGate(externalIdsRequested) val (entriesToCreate, entriesToUpdate) = entries.partition { it.bpn == null } // Create or update (LSAs) in Pool @@ -86,9 +89,9 @@ class SyncService( } } - private fun syncSites(externalIdsRequested: Collection) { + private fun syncSites(externalIdsRequested: Set) { // Retrieve business partners (LSA) from Gate - val entries = getSitesFromGate(externalIdsRequested) + val entries = getSiteInfosFromGate(externalIdsRequested) val (entriesToCreate, entriesToUpdate) = entries.partition { it.bpn == null } // Create or update (LSAs) in Pool @@ -100,9 +103,9 @@ class SyncService( } } - private fun syncAddresses(externalIdsRequested: Collection) { + private fun syncAddresses(externalIdsRequested: Set) { // Retrieve business partners (LSA) from Gate - val entries = getAddressesFromGate(externalIdsRequested) + val entries = getAddressInfosFromGate(externalIdsRequested) val (entriesToCreate, entriesToUpdate) = entries.partition { it.bpn == null } // Create or update (LSAs) in Pool @@ -114,7 +117,50 @@ class SyncService( } } - private fun getLegalEntitiesFromGate(externalIds: Collection): Collection { + + private fun getLegalEntityInfosFromGate(externalIds: Set): Collection { + val entries = getLegalEntitiesInputFromGate(externalIds) + val bpnByExternalId = getBpnByExternalIdFromGate(LsaType.LegalEntity, externalIds) + + return entries.map { + GateLegalEntityInfo( + legalEntity = it.legalEntity, + externalId = it.externalId, + bpn = bpnByExternalId[it.externalId] + ) + } + } + + private fun getSiteInfosFromGate(externalIds: Set): Collection { + val entries = getSitesInputFromGate(externalIds) + val bpnByExternalId = getBpnByExternalIdFromGate(LsaType.Site, externalIds) + + return entries.map { + GateSiteInfo( + site = it.site, + externalId = it.externalId, + legalEntityExternalId = it.legalEntityExternalId, + bpn = bpnByExternalId[it.externalId] + ) + } + } + + private fun getAddressInfosFromGate(externalIds: Set): Collection { + val entries = getAddressesInputFromGate(externalIds) + val bpnByExternalId = getBpnByExternalIdFromGate(LsaType.Address, externalIds) + + return entries.map { + GateAddressInfo( + address = it.address, + externalId = it.externalId, + legalEntityExternalId = it.legalEntityExternalId, + siteExternalId = it.siteExternalId, + bpn = bpnByExternalId[it.externalId] + ) + } + } + + private fun getLegalEntitiesInputFromGate(externalIds: Set): Collection { if (externalIds.isEmpty()) { return emptyList() } @@ -127,7 +173,7 @@ class SyncService( return response.content } - private fun getSitesFromGate(externalIds: Collection): Collection { + private fun getSitesInputFromGate(externalIds: Set): Collection { if (externalIds.isEmpty()) { return emptyList() } @@ -140,7 +186,7 @@ class SyncService( return response.content } - private fun getAddressesFromGate(externalIds: Collection): Collection { + private fun getAddressesInputFromGate(externalIds: Set): Collection { if (externalIds.isEmpty()) { return emptyList() } @@ -153,7 +199,24 @@ class SyncService( return response.content } - private fun createLegalEntitiesInPool(entriesToCreate: Collection) { + private fun getBpnByExternalIdFromGate(lsaType: LsaType, externalIds: Set): Map { + if (externalIds.isEmpty()) { + return emptyMap() + } + // TODO use pagination properly + val page = gateClient.sharingState().getSharingStates( + lsaType = lsaType, + externalIds = externalIds, + paginationRequest = PaginationRequest(0, 100) + ) + return page.content + .associateBy { it.externalId } + .filter { it.value.bpn != null } + .mapValues { it.value.bpn!! } + } + + + private fun createLegalEntitiesInPool(entriesToCreate: Collection) { val createRequests = entriesToCreate.map { LegalEntityPartnerCreateRequest( legalEntity = it.legalEntity, @@ -167,7 +230,7 @@ class SyncService( handleLegalEntityCreateResponse(responseWrapper) } - private fun updateLegalEntitiesInPool(entriesToUpdate: Collection) { + private fun updateLegalEntitiesInPool(entriesToUpdate: Collection) { val updateRequests = entriesToUpdate.map { LegalEntityPartnerUpdateRequest( legalEntity = it.legalEntity, @@ -182,13 +245,12 @@ class SyncService( handleLegalEntityUpdateResponse(responseWrapper, externalIdByBpn) } - private fun createSitesInPool(entriesToCreate: List) { - val leParentsByExternalId = entriesToCreate + private fun createSitesInPool(entriesToCreate: Collection) { + val leParentBpnByExternalId = entriesToCreate .map { it.legalEntityExternalId } - .let { getLegalEntitiesFromGate(it) } - .associateBy { it.externalId } + .let { getBpnByExternalIdFromGate(LsaType.LegalEntity, it.toSet()) } val createRequests = entriesToCreate.mapNotNull { entry -> - leParentsByExternalId[entry.legalEntityExternalId]?.bpn + leParentBpnByExternalId[entry.legalEntityExternalId] ?.let { leParentBpn -> SitePartnerCreateRequest( site = entry.site, @@ -210,7 +272,7 @@ class SyncService( handleSiteCreateResponse(responseWrapper) } - private fun updateSitesInPool(entriesToUpdate: List) { + private fun updateSitesInPool(entriesToUpdate: Collection) { val updateRequests = entriesToUpdate.map { SitePartnerUpdateRequest( site = it.site, @@ -225,13 +287,12 @@ class SyncService( handleSiteUpdateResponse(responseWrapper, externalIdByBpn) } - private fun createAddressesInPool(entriesToCreate: List) { - val leParentsByExternalId = entriesToCreate + private fun createAddressesInPool(entriesToCreate: Collection) { + val leParentBpnByExternalId = entriesToCreate .mapNotNull { it.legalEntityExternalId } - .let { getLegalEntitiesFromGate(it) } - .associateBy { it.externalId } + .let { getBpnByExternalIdFromGate(LsaType.LegalEntity, it.toSet()) } val leParentsCreateRequests = entriesToCreate.mapNotNull { entry -> - leParentsByExternalId[entry.legalEntityExternalId]?.bpn + leParentBpnByExternalId[entry.legalEntityExternalId] ?.let { leParentBpn -> AddressPartnerCreateRequest( address = entry.address, @@ -241,12 +302,11 @@ class SyncService( } } - val siteParentsByExternalId = entriesToCreate + val siteParentBpnByExternalId = entriesToCreate .mapNotNull { it.siteExternalId } - .let { getSitesFromGate(it) } - .associateBy { it.externalId } + .let { getBpnByExternalIdFromGate(LsaType.Site, it.toSet()) } val siteParentsCreateRequests = entriesToCreate.mapNotNull { entry -> - siteParentsByExternalId[entry.siteExternalId]?.bpn + siteParentBpnByExternalId[entry.siteExternalId] ?.let { siteParentBpn -> AddressPartnerCreateRequest( address = entry.address, @@ -269,7 +329,7 @@ class SyncService( handleAddressCreateResponse(responseWrapper) } - private fun updateAddressesInPool(entriesToUpdate: List) { + private fun updateAddressesInPool(entriesToUpdate: Collection) { val updateRequests = entriesToUpdate.map { AddressPartnerUpdateRequest( address = it.address, From acee40366de017c65dfffa1d3c694799e9749041 Mon Sep 17 00:00:00 2001 From: Martin Kaeser Date: Thu, 11 May 2023 18:43:58 +0200 Subject: [PATCH 3/8] feat(gate): SharingStateController mock implementation which calls CDQ input --- .../gate/controller/SharingStateController.kt | 62 +++++++++++++++++-- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt index f46226da6..9f4f49157 100644 --- a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt @@ -24,22 +24,74 @@ import org.eclipse.tractusx.bpdm.common.dto.request.PaginationRequest import org.eclipse.tractusx.bpdm.common.dto.response.PageResponse import org.eclipse.tractusx.bpdm.gate.api.GateSharingStateApi import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateDto +import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateType import org.eclipse.tractusx.bpdm.gate.api.model.response.LsaType +import org.eclipse.tractusx.bpdm.gate.service.AddressService +import org.eclipse.tractusx.bpdm.gate.service.LegalEntityService +import org.eclipse.tractusx.bpdm.gate.service.SiteService import org.springframework.web.bind.annotation.RestController @RestController -class SharingStateController : GateSharingStateApi { +class SharingStateController( + val legalEntityService: LegalEntityService, + val siteService: SiteService, + val addressService: AddressService +) : GateSharingStateApi { private val logger = KotlinLogging.logger { } override fun getSharingStates(paginationRequest: PaginationRequest, lsaType: LsaType?, externalIds: Collection?): PageResponse { + // TODO Replace mock implementation using persistence: + // For now only the BPN is collected from CDQ input. If there is none, no sharing status is returned. + // For now lsaType is required! + lsaType ?: throw IllegalArgumentException("lsaType is required") + externalIds ?: throw IllegalArgumentException("externalIds is required") + + val sharingStates = when (lsaType) { + LsaType.LegalEntity -> + legalEntityService.getLegalEntities(externalIds = externalIds, limit = paginationRequest.size, startAfter = null).content + .filter { it.bpn != null } + .map { + SharingStateDto( + lsaType = lsaType, + externalId = it.externalId, + bpn = it.bpn, + sharingStateType = SharingStateType.Success + ) + } + + LsaType.Site -> + siteService.getSites(externalIds = externalIds, limit = paginationRequest.size, startAfter = null).content + .filter { it.bpn != null } + .map { + SharingStateDto( + lsaType = lsaType, + externalId = it.externalId, + bpn = it.bpn, + sharingStateType = SharingStateType.Success + ) + } + + LsaType.Address -> + addressService.getAddresses(externalIds = externalIds, limit = paginationRequest.size, startAfter = null).content + .filter { it.bpn != null } + .map { + SharingStateDto( + lsaType = lsaType, + externalId = it.externalId, + bpn = it.bpn, + sharingStateType = SharingStateType.Success + ) + } + } + // TODO Not yet implemented return PageResponse( - totalElements = 0, - totalPages = 0, + totalElements = sharingStates.size.toLong(), + totalPages = if (sharingStates.size > 0) 1 else 0, page = 0, - contentSize = 0, - content = emptyList() + contentSize = sharingStates.size, + content = sharingStates ) } From 1ce846088fd5d4bba4dd1b80d9ee684fd147c2ab Mon Sep 17 00:00:00 2001 From: rschneider <97682836+rainer-exxcellent@users.noreply.github.com> Date: Wed, 17 May 2023 07:49:04 +0200 Subject: [PATCH 4/8] feat(sharing state): #163 implement SharingStateController with persistence --- .../gate/controller/SharingStateController.kt | 61 ++----------- .../tractusx/bpdm/gate/entity/SharingState.kt | 61 +++++++++++++ .../repository/SharingStaterRepository.kt | 35 ++++++++ .../bpdm/gate/service/SharingStateService.kt | 88 +++++++++++++++++++ .../V0_0_2_0__create_sharingstate_table.sql | 28 ++++++ .../gate/controller/ChangeLogControllerIT.kt | 53 +++-------- .../controller/SharingStateControllerIT.kt | 70 +++++++++++++++ .../tractusx/bpdm/gate/util/DbTestHelpers.kt | 54 ++++++++++++ 8 files changed, 353 insertions(+), 97 deletions(-) create mode 100644 bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/entity/SharingState.kt create mode 100644 bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStaterRepository.kt create mode 100644 bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/SharingStateService.kt create mode 100644 bpdm-gate/src/main/resources/db/migration/V0_0_2_0__create_sharingstate_table.sql create mode 100644 bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateControllerIT.kt create mode 100644 bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/util/DbTestHelpers.kt diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt index 9f4f49157..ca645c173 100644 --- a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt @@ -24,79 +24,28 @@ import org.eclipse.tractusx.bpdm.common.dto.request.PaginationRequest import org.eclipse.tractusx.bpdm.common.dto.response.PageResponse import org.eclipse.tractusx.bpdm.gate.api.GateSharingStateApi import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateDto -import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateType import org.eclipse.tractusx.bpdm.gate.api.model.response.LsaType -import org.eclipse.tractusx.bpdm.gate.service.AddressService -import org.eclipse.tractusx.bpdm.gate.service.LegalEntityService -import org.eclipse.tractusx.bpdm.gate.service.SiteService +import org.eclipse.tractusx.bpdm.gate.service.SharingStateService import org.springframework.web.bind.annotation.RestController @RestController class SharingStateController( - val legalEntityService: LegalEntityService, - val siteService: SiteService, - val addressService: AddressService + val sharingStateService: SharingStateService ) : GateSharingStateApi { private val logger = KotlinLogging.logger { } override fun getSharingStates(paginationRequest: PaginationRequest, lsaType: LsaType?, externalIds: Collection?): PageResponse { - // TODO Replace mock implementation using persistence: - // For now only the BPN is collected from CDQ input. If there is none, no sharing status is returned. - // For now lsaType is required! + lsaType ?: throw IllegalArgumentException("lsaType is required") externalIds ?: throw IllegalArgumentException("externalIds is required") - val sharingStates = when (lsaType) { - LsaType.LegalEntity -> - legalEntityService.getLegalEntities(externalIds = externalIds, limit = paginationRequest.size, startAfter = null).content - .filter { it.bpn != null } - .map { - SharingStateDto( - lsaType = lsaType, - externalId = it.externalId, - bpn = it.bpn, - sharingStateType = SharingStateType.Success - ) - } - - LsaType.Site -> - siteService.getSites(externalIds = externalIds, limit = paginationRequest.size, startAfter = null).content - .filter { it.bpn != null } - .map { - SharingStateDto( - lsaType = lsaType, - externalId = it.externalId, - bpn = it.bpn, - sharingStateType = SharingStateType.Success - ) - } - - LsaType.Address -> - addressService.getAddresses(externalIds = externalIds, limit = paginationRequest.size, startAfter = null).content - .filter { it.bpn != null } - .map { - SharingStateDto( - lsaType = lsaType, - externalId = it.externalId, - bpn = it.bpn, - sharingStateType = SharingStateType.Success - ) - } - } - - // TODO Not yet implemented - return PageResponse( - totalElements = sharingStates.size.toLong(), - totalPages = if (sharingStates.size > 0) 1 else 0, - page = 0, - contentSize = sharingStates.size, - content = sharingStates - ) + return sharingStateService.findSharingStates(paginationRequest, lsaType, externalIds) } override fun upsertSharingState(request: SharingStateDto) { // TODO Not yet implemented logger.info { "upsertSharingState() called with $request" } + sharingStateService.upsertSharingState(request) } } diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/entity/SharingState.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/entity/SharingState.kt new file mode 100644 index 000000000..1abd98360 --- /dev/null +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/entity/SharingState.kt @@ -0,0 +1,61 @@ +/******************************************************************************* + * 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.entity + +import jakarta.persistence.* +import org.eclipse.tractusx.bpdm.common.model.BaseEntity +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.LsaType +import java.time.LocalDateTime + + +@Entity +@Table(name = "sharing_states") +class SharingState( + + @Column(name = "external_id", nullable = false) + var externalId: String, + + @Enumerated(EnumType.STRING) + @Column(name = "lsa_type", nullable = false) + var lsaType: LsaType, + + @Enumerated(EnumType.STRING) + @Column(name = "sharing_state_type", nullable = false) + var sharingStateType: SharingStateType, + + @Enumerated(EnumType.STRING) + @Column(name = "sharing_error_code", nullable = true) + var sharingErrorCode: BusinessPartnerSharingError?, + + @Column(name = "sharing_error_message", nullable = true) + var sharingErrorMessage: String? = null, + + @Column(name = "bpn", nullable = true) + var bpn: String? = null, + + @Column(name = "sharing_process_started", nullable = true) + var sharingProcessStarted: LocalDateTime? = null + +) : BaseEntity() + + + diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStaterRepository.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStaterRepository.kt new file mode 100644 index 000000000..04e27ac5e --- /dev/null +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStaterRepository.kt @@ -0,0 +1,35 @@ +/******************************************************************************* + * 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.repository + +import org.eclipse.tractusx.bpdm.gate.api.model.response.LsaType +import org.eclipse.tractusx.bpdm.gate.entity.SharingState +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.springframework.data.repository.CrudRepository +import org.springframework.data.repository.PagingAndSortingRepository + +interface SharingStaterRepository : PagingAndSortingRepository, CrudRepository { + + fun findByExternalIdAndLsaType(externalId: String, businessPartnerType: LsaType): SharingState? + + fun findByExternalIdInAndLsaType(externalIds: Collection, businessPartnerType: LsaType, pageable: Pageable): Page + +} \ No newline at end of file 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 new file mode 100644 index 000000000..8fccf99e8 --- /dev/null +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/SharingStateService.kt @@ -0,0 +1,88 @@ +/******************************************************************************* + * 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.service + +import org.eclipse.tractusx.bpdm.common.dto.request.PaginationRequest +import org.eclipse.tractusx.bpdm.common.dto.response.PageResponse +import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateDto +import org.eclipse.tractusx.bpdm.gate.api.model.response.LsaType +import org.eclipse.tractusx.bpdm.gate.entity.SharingState +import org.eclipse.tractusx.bpdm.gate.repository.SharingStaterRepository +import org.springframework.data.domain.PageRequest +import org.springframework.stereotype.Service + +@Service +class SharingStateService(private val stateRepository: SharingStaterRepository) { + + fun upsertSharingState(request: SharingStateDto) { + + val sharingState = this.stateRepository.findByExternalIdAndLsaType(request.externalId, request.lsaType) + if (sharingState == null) { + insertSharingState(request) + } else { + updateSharingState(sharingState, request) + } + } + + private fun insertSharingState(dto: SharingStateDto) { + + this.stateRepository.save( + SharingState( + externalId = dto.externalId, + lsaType = dto.lsaType, + sharingStateType = dto.sharingStateType, + sharingErrorCode = dto.sharingErrorCode, + sharingErrorMessage = dto.sharingErrorMessage, + bpn = dto.bpn, + sharingProcessStarted = dto.sharingProcessStarted + ) + ) + } + + private fun updateSharingState(entity: SharingState, dto: SharingStateDto) { + + entity.sharingStateType = dto.sharingStateType + entity.sharingErrorCode = dto.sharingErrorCode + entity.sharingErrorMessage = dto.sharingErrorMessage + entity.bpn = dto.bpn + entity.sharingProcessStarted = dto.sharingProcessStarted + + this.stateRepository.save(entity) + } + + fun findSharingStates(paginationRequest: PaginationRequest, lsaType: LsaType, externalIds: Collection): PageResponse { + + val pageable = PageRequest.of(paginationRequest.page, paginationRequest.size) + val page = this.stateRepository.findByExternalIdInAndLsaType(externalIds, lsaType, pageable) + + return page.toDto(page.content.map { + SharingStateDto( + externalId = it.externalId, + lsaType = it.lsaType, + sharingStateType = it.sharingStateType, + sharingErrorCode = it.sharingErrorCode, + sharingErrorMessage = it.sharingErrorMessage, + bpn = it.bpn, + sharingProcessStarted = it.sharingProcessStarted + ) + }) + + } +} \ No newline at end of file diff --git a/bpdm-gate/src/main/resources/db/migration/V0_0_2_0__create_sharingstate_table.sql b/bpdm-gate/src/main/resources/db/migration/V0_0_2_0__create_sharingstate_table.sql new file mode 100644 index 000000000..97d53665c --- /dev/null +++ b/bpdm-gate/src/main/resources/db/migration/V0_0_2_0__create_sharingstate_table.sql @@ -0,0 +1,28 @@ + +CREATE TABLE sharing_states +( + id BIGINT NOT NULL, + uuid UUID NOT NULL, + created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + external_id VARCHAR(255) NOT NULL, + lsa_type VARCHAR(255) NOT NULL, + sharing_state_type VARCHAR(255) NOT NULL, + sharing_error_code VARCHAR(255), + sharing_error_message VARCHAR(255), + bpn VARCHAR(255), + sharing_process_started timestamp with time zone, + CONSTRAINT pk_sharing_states PRIMARY KEY (id) +); + +ALTER TABLE sharing_states + ADD CONSTRAINT uc_sharing_states_uuid UNIQUE (uuid); +ALTER TABLE sharing_states + ADD CONSTRAINT uc_sharing_states_externalId_lsa_type UNIQUE (external_id, lsa_type); + +alter table sharing_states + alter column created_at type timestamp with time zone using created_at at time zone 'UTC'; +alter table sharing_states + alter column updated_at type timestamp with time zone using updated_at at time zone 'UTC'; + alter table sharing_states + alter column sharing_process_started type timestamp with time zone using sharing_process_started at time zone 'UTC'; diff --git a/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/ChangeLogControllerIT.kt b/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/ChangeLogControllerIT.kt index b9d073725..9b7888a4b 100644 --- a/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/ChangeLogControllerIT.kt +++ b/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/ChangeLogControllerIT.kt @@ -42,8 +42,6 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.github.tomakehurst.wiremock.client.WireMock.* import com.github.tomakehurst.wiremock.core.WireMockConfiguration import com.github.tomakehurst.wiremock.junit5.WireMockExtension -import jakarta.persistence.EntityManager -import jakarta.persistence.EntityManagerFactory import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.RecursiveComparisonAssert import org.eclipse.tractusx.bpdm.common.dto.request.PaginationRequest @@ -54,9 +52,7 @@ import org.eclipse.tractusx.bpdm.gate.api.model.response.ChangelogResponse import org.eclipse.tractusx.bpdm.gate.api.model.response.ErrorInfo import org.eclipse.tractusx.bpdm.gate.config.SaasConfigProperties import org.eclipse.tractusx.bpdm.gate.entity.ChangelogEntry - import org.eclipse.tractusx.bpdm.gate.util.* - import org.eclipse.tractusx.bpdm.gate.util.CommonValues.lsaTypeParam import org.eclipse.tractusx.bpdm.gate.util.CommonValues.lsaTypeParamNotFound import org.junit.jupiter.api.BeforeEach @@ -68,7 +64,6 @@ import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.ContextConfiguration import org.springframework.test.context.DynamicPropertyRegistry import org.springframework.test.context.DynamicPropertySource - import java.time.Instant @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @@ -78,7 +73,7 @@ internal class ChangeLogControllerIT @Autowired constructor( val gateClient: GateClient, private val objectMapper: ObjectMapper, val saasConfigProperties: SaasConfigProperties, - entityManagerFactory: EntityManagerFactory, + private val testHelpers: DbTestHelpers, ) { companion object { @RegisterExtension @@ -96,13 +91,9 @@ internal class ChangeLogControllerIT @Autowired constructor( val instant = Instant.now() - val BPDM_DB_SCHEMA_NAME: String = "bpdmgate" - - val em: EntityManager = entityManagerFactory.createEntityManager() - @BeforeEach fun beforeEach() { - truncateDbTables() + testHelpers.truncateDbTables() wireMockServer.resetAll() mockSaas() createChangeLogs() @@ -124,7 +115,6 @@ internal class ChangeLogControllerIT @Autowired constructor( } - /** * Given externalId does not exist in database * When getting changeLog by external id @@ -145,13 +135,15 @@ internal class ChangeLogControllerIT @Autowired constructor( .isEqualTo(emptyList()) assertRecursively(searchResult.errors) - .isEqualTo(listOf( - ErrorInfo( - ChangeLogOutputError.ExternalIdNotFound, - "NONEXIST not found", - "NONEXIST") - )) - + .isEqualTo( + listOf( + ErrorInfo( + ChangeLogOutputError.ExternalIdNotFound, + "NONEXIST not found", + "NONEXIST" + ) + ) + ) } @@ -241,7 +233,6 @@ internal class ChangeLogControllerIT @Autowired constructor( ) - // mock "get parent legal entities" wireMockServer.stubFor( get(urlPathMatching(EndpointValues.SAAS_MOCK_BUSINESS_PARTNER_PATH)) @@ -353,26 +344,6 @@ internal class ChangeLogControllerIT @Autowired constructor( ) } - fun truncateDbTables() { - em.transaction.begin() - - em.createNativeQuery( - """ - DO $$ DECLARE table_names RECORD; - BEGIN - FOR table_names IN SELECT table_name - FROM information_schema.tables - WHERE table_schema='${BPDM_DB_SCHEMA_NAME}' - AND table_name NOT IN ('flyway_schema_history') - LOOP - EXECUTE format('TRUNCATE TABLE ${BPDM_DB_SCHEMA_NAME}.%I CONTINUE IDENTITY CASCADE;', table_names.table_name); - END LOOP; - END $$; - """.trimIndent() - ).executeUpdate() - - em.transaction.commit() - } fun assertRecursively(actual: T): RecursiveComparisonAssert<*> { return assertThat(actual) @@ -387,7 +358,7 @@ internal class ChangeLogControllerIT @Autowired constructor( * Retains the order: All response objects will be in the same order as their request counterparts * Assumption: Changelog entities have unique indexes among them each */ - fun createChangeLogs(){ + fun createChangeLogs() { val addresses = listOf( RequestValues.addressGateInputRequest1 ) 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 new file mode 100644 index 000000000..63e3695cd --- /dev/null +++ b/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateControllerIT.kt @@ -0,0 +1,70 @@ +/******************************************************************************* + * 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.controller + +import org.assertj.core.api.Assertions +import org.eclipse.tractusx.bpdm.common.dto.request.PaginationRequest +import org.eclipse.tractusx.bpdm.gate.api.client.GateClient +import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateDto +import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateType +import org.eclipse.tractusx.bpdm.gate.api.model.response.LsaType +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.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) +@ActiveProfiles("test") +@ContextConfiguration(initializers = [PostgreSQLContextInitializer::class]) +class SharingStateControllerIT @Autowired constructor( + + private val testHelpers: DbTestHelpers, + val gateClient: GateClient, +) { + + @BeforeEach + fun beforeEach() { + testHelpers.truncateDbTables() + } + + /** + * Given externalId exists in database + * When getting changeLog by external id + * Then changeLog mapped to the catena data model should be returned + */ + @Test + fun `get changeLog by external id`() { + + val state1 = SharingStateDto( + lsaType = LsaType.Address, externalId = "exId1", sharingStateType = SharingStateType.Success, + sharingErrorCode = null, sharingProcessStarted = null, sharingErrorMessage = null, bpn = null + ) + gateClient.sharingState().upsertSharingState(state1) + + val searchResult = gateClient.sharingState().getSharingStates(PaginationRequest(), LsaType.Address, listOf("exId1")) + Assertions.assertThat(searchResult.content.size).isEqualTo(1) + val searchResult2 = gateClient.sharingState().getSharingStates(PaginationRequest(), LsaType.Site, listOf("exId1")) + Assertions.assertThat(searchResult2.content.size).isEqualTo(0) + } +} \ No newline at end of file diff --git a/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/util/DbTestHelpers.kt b/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/util/DbTestHelpers.kt new file mode 100644 index 000000000..833fb5482 --- /dev/null +++ b/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/util/DbTestHelpers.kt @@ -0,0 +1,54 @@ +/******************************************************************************* + * 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.util + +import jakarta.persistence.EntityManager +import jakarta.persistence.EntityManagerFactory +import org.springframework.stereotype.Component + +private const val BPDM_DB_SCHEMA_NAME: String = "bpdmgate" + +@Component +class DbTestHelpers(entityManagerFactory: EntityManagerFactory) { + + val em: EntityManager = entityManagerFactory.createEntityManager() + + fun truncateDbTables() { + em.transaction.begin() + + em.createNativeQuery( + """ + DO $$ DECLARE table_names RECORD; + BEGIN + FOR table_names IN SELECT table_name + FROM information_schema.tables + WHERE table_schema='$BPDM_DB_SCHEMA_NAME' + AND table_name NOT IN ('flyway_schema_history') + LOOP + EXECUTE format('TRUNCATE TABLE $BPDM_DB_SCHEMA_NAME.%I CONTINUE IDENTITY CASCADE;', table_names.table_name); + END LOOP; + END $$; + """.trimIndent() + ).executeUpdate() + + em.transaction.commit() + } + +} \ No newline at end of file From 9f1b0d635e7437924e1f46f1bc9b170807497b77 Mon Sep 17 00:00:00 2001 From: rschneider <97682836+rainer-exxcellent@users.noreply.github.com> Date: Tue, 23 May 2023 09:27:20 +0200 Subject: [PATCH 5/8] feat(sharing state): #163 add testcases for SharingStateController --- .../gate/controller/SharingStateController.kt | 2 +- .../controller/SharingStateControllerIT.kt | 122 ++++++++++++++++-- 2 files changed, 109 insertions(+), 15 deletions(-) diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt index ca645c173..3d90a3239 100644 --- a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt @@ -44,7 +44,7 @@ class SharingStateController( } override fun upsertSharingState(request: SharingStateDto) { - // TODO Not yet implemented + logger.info { "upsertSharingState() called with $request" } sharingStateService.upsertSharingState(request) } 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 63e3695cd..647512b06 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 @@ -19,9 +19,11 @@ package org.eclipse.tractusx.bpdm.gate.controller -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat import org.eclipse.tractusx.bpdm.common.dto.request.PaginationRequest 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.model.SharingStateDto import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateType import org.eclipse.tractusx.bpdm.gate.api.model.response.LsaType @@ -33,6 +35,7 @@ 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 java.time.LocalDateTime @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("test") @@ -48,23 +51,114 @@ class SharingStateControllerIT @Autowired constructor( testHelpers.truncateDbTables() } + @Test + fun `insert and get sharing states `() { + + val stateAddress = insertSharingStateSuccess(LsaType.Address, externalId = "exIdAddress") + val stateSite = insertSharingStateSuccess(LsaType.Site, externalId = "exIdSite") + insertSharingStateSuccess(LsaType.LegalEntity, externalId = "exIdEntity1") + val stateLegalEntity2 = insertSharingStateSuccess(LsaType.LegalEntity, externalId = "exIdEntity2") + + val searchAddress = readSharingStates(LsaType.Address, "exIdAddress") + assertThat(searchAddress).hasSize(1) + assertThat(searchAddress.first()).isEqualTo(stateAddress) + + val searchSites = readSharingStates(LsaType.Site, "exIdSite") + assertThat(searchSites).hasSize(1) + assertThat(searchSites.first()).isEqualTo(stateSite) + + val searchWrongId = readSharingStates(LsaType.Address, "exIdEntity") + assertThat(searchWrongId).hasSize(0) + + val searchEntityMultiple = readSharingStates(LsaType.LegalEntity, "exIdEntity1", "exIdEntity2") + assertThat(searchEntityMultiple).hasSize(2) + + val searchEntitySingle = readSharingStates(LsaType.LegalEntity, "exIdEntity2") + assertThat(searchEntitySingle).hasSize(1) + assertThat(searchEntitySingle.first()).isEqualTo(stateLegalEntity2) + + } + + @Test + fun `insert and get sharing states with error code`() { + + val stateAddress1 = insertSharingStateError(LsaType.Address, externalId = "exIdAddress1", errorCode = SharingTimeout) + insertSharingStateError(LsaType.Address, externalId = "exIdAddress2", errorCode = SharingProcessError) + insertSharingStateError(LsaType.Address, externalId = "exIdAddress3", errorCode = BpnNotInPool) + + val searchAddress = readSharingStates(LsaType.Address, "exIdAddress1") + assertThat(searchAddress).hasSize(1) + assertThat(searchAddress.first()).isEqualTo(stateAddress1) + + } + + @Test + fun `insert and update states`() { + + val stateAddress1 = insertSharingStateError(LsaType.Address, externalId = "exIdAddress1", errorCode = SharingTimeout) + insertSharingStateError(LsaType.Address, externalId = "exIdAddress2", errorCode = SharingProcessError) + insertSharingStateError(LsaType.Address, externalId = "exIdAddress3", errorCode = BpnNotInPool) + + val searchAddress = readSharingStates(LsaType.Address, "exIdAddress1") + assertThat(searchAddress).hasSize(1) + assertThat(searchAddress.first()).isEqualTo(stateAddress1) + + val updatedAddress1 = stateAddress1.copy( + sharingStateType = SharingStateType.Success, + sharingErrorCode = BpnNotInPool, + sharingProcessStarted = LocalDateTime.now().withNano(0), + sharingErrorMessage = "Changed ", + bpn = null + ) + + gateClient.sharingState().upsertSharingState(updatedAddress1) + + val readUpdatedAddress = readSharingStates(LsaType.Address, "exIdAddress1") + assertThat(readUpdatedAddress).hasSize(1) + assertThat(readUpdatedAddress.first()).isEqualTo(updatedAddress1) + } + + /** - * Given externalId exists in database - * When getting changeLog by external id - * Then changeLog mapped to the catena data model should be returned + * Insert Sharing State only with required fields filled */ - @Test - fun `get changeLog by external id`() { + fun insertSharingStateSuccess(lsaType: LsaType, externalId: String): SharingStateDto { + + val newState = SharingStateDto( + lsaType = lsaType, + externalId = externalId, + sharingStateType = SharingStateType.Success, + sharingErrorCode = null, + sharingProcessStarted = null, + sharingErrorMessage = null, + bpn = null + ) + gateClient.sharingState().upsertSharingState(newState) + return newState + } + + /** + * Insert Sharing State with all Fields Field + */ + fun insertSharingStateError(lsaType: LsaType, externalId: String, errorCode: BusinessPartnerSharingError): SharingStateDto { - val state1 = SharingStateDto( - lsaType = LsaType.Address, externalId = "exId1", sharingStateType = SharingStateType.Success, - sharingErrorCode = null, sharingProcessStarted = null, sharingErrorMessage = null, bpn = null + val newState = SharingStateDto( + lsaType = lsaType, + externalId = externalId, + sharingStateType = SharingStateType.Error, + sharingErrorCode = errorCode, + sharingProcessStarted = LocalDateTime.now().withNano(0), + sharingErrorMessage = "Error in $lsaType with external id $externalId", + bpn = "BPN" + externalId ) - gateClient.sharingState().upsertSharingState(state1) + gateClient.sharingState().upsertSharingState(newState) + return newState + } + + fun readSharingStates(lsaType: LsaType, vararg externalIds: String): Collection { - val searchResult = gateClient.sharingState().getSharingStates(PaginationRequest(), LsaType.Address, listOf("exId1")) - Assertions.assertThat(searchResult.content.size).isEqualTo(1) - val searchResult2 = gateClient.sharingState().getSharingStates(PaginationRequest(), LsaType.Site, listOf("exId1")) - Assertions.assertThat(searchResult2.content.size).isEqualTo(0) + return gateClient.sharingState().getSharingStates(PaginationRequest(), lsaType, externalIds.asList()).content } + + } \ No newline at end of file From d4c226a938e0e6af27e7171d077374ea7e2c9bbb Mon Sep 17 00:00:00 2001 From: Martin Kaeser Date: Tue, 23 May 2023 14:43:46 +0200 Subject: [PATCH 6/8] feat(gate: sharing state): Update sharingProcessStarted only if not null --- .../eclipse/tractusx/bpdm/gate/service/SharingStateService.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 8fccf99e8..52ffdc269 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 @@ -62,7 +62,9 @@ class SharingStateService(private val stateRepository: SharingStaterRepository) entity.sharingErrorCode = dto.sharingErrorCode entity.sharingErrorMessage = dto.sharingErrorMessage entity.bpn = dto.bpn - entity.sharingProcessStarted = dto.sharingProcessStarted + if (dto.sharingProcessStarted != null) { + entity.sharingProcessStarted = dto.sharingProcessStarted + } this.stateRepository.save(entity) } From cc51219a669326d7bc0dac66a9511c9897dfaf51 Mon Sep 17 00:00:00 2001 From: Martin Kaeser Date: Tue, 23 May 2023 17:24:59 +0200 Subject: [PATCH 7/8] feat(gate: sharing state): Make lsaType, externalIds optional when filtering sharing state --- .../gate/controller/SharingStateController.kt | 3 -- ...epository.kt => SharingStateRepository.kt} | 30 +++++++++++++++---- .../bpdm/gate/service/SharingStateService.kt | 14 +++++---- 3 files changed, 34 insertions(+), 13 deletions(-) rename bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/{SharingStaterRepository.kt => SharingStateRepository.kt} (55%) diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt index 3d90a3239..0a847ee29 100644 --- a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt @@ -37,9 +37,6 @@ class SharingStateController( override fun getSharingStates(paginationRequest: PaginationRequest, lsaType: LsaType?, externalIds: Collection?): PageResponse { - lsaType ?: throw IllegalArgumentException("lsaType is required") - externalIds ?: throw IllegalArgumentException("externalIds is required") - return sharingStateService.findSharingStates(paginationRequest, lsaType, externalIds) } diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStaterRepository.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStateRepository.kt similarity index 55% rename from bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStaterRepository.kt rename to bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStateRepository.kt index 04e27ac5e..c530e1d89 100644 --- a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStaterRepository.kt +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStateRepository.kt @@ -21,15 +21,35 @@ package org.eclipse.tractusx.bpdm.gate.repository import org.eclipse.tractusx.bpdm.gate.api.model.response.LsaType import org.eclipse.tractusx.bpdm.gate.entity.SharingState -import org.springframework.data.domain.Page -import org.springframework.data.domain.Pageable +import org.springframework.data.jpa.domain.Specification +import org.springframework.data.jpa.repository.JpaSpecificationExecutor import org.springframework.data.repository.CrudRepository import org.springframework.data.repository.PagingAndSortingRepository -interface SharingStaterRepository : PagingAndSortingRepository, CrudRepository { +interface SharingStateRepository : PagingAndSortingRepository, CrudRepository, JpaSpecificationExecutor { - fun findByExternalIdAndLsaType(externalId: String, businessPartnerType: LsaType): SharingState? + object Specs { + /** + * Restrict to entries with any one of the given externalIds; ignore if null + */ + fun byExternalIdsIn(externalIds: Collection?) = + Specification { root, _, _ -> + externalIds?.let { + root.get(SharingState::externalId.name).`in`(externalIds) + } + } + + /** + * Restrict to entries with the given lsaType; ignore if null + */ + fun byLsaType(lsaType: LsaType?) = + Specification { root, _, builder -> + lsaType?.let { + builder.equal(root.get(SharingState::lsaType.name), lsaType) + } + } + } - fun findByExternalIdInAndLsaType(externalIds: Collection, businessPartnerType: LsaType, pageable: Pageable): Page + fun findByExternalIdAndLsaType(externalId: String, businessPartnerType: LsaType): SharingState? } \ No newline at end of file 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 52ffdc269..65c6d67c9 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 @@ -24,12 +24,15 @@ import org.eclipse.tractusx.bpdm.common.dto.response.PageResponse import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateDto import org.eclipse.tractusx.bpdm.gate.api.model.response.LsaType import org.eclipse.tractusx.bpdm.gate.entity.SharingState -import org.eclipse.tractusx.bpdm.gate.repository.SharingStaterRepository +import org.eclipse.tractusx.bpdm.gate.repository.SharingStateRepository +import org.eclipse.tractusx.bpdm.gate.repository.SharingStateRepository.Specs.byExternalIdsIn +import org.eclipse.tractusx.bpdm.gate.repository.SharingStateRepository.Specs.byLsaType import org.springframework.data.domain.PageRequest +import org.springframework.data.jpa.domain.Specification import org.springframework.stereotype.Service @Service -class SharingStateService(private val stateRepository: SharingStaterRepository) { +class SharingStateService(private val stateRepository: SharingStateRepository) { fun upsertSharingState(request: SharingStateDto) { @@ -69,10 +72,11 @@ class SharingStateService(private val stateRepository: SharingStaterRepository) this.stateRepository.save(entity) } - fun findSharingStates(paginationRequest: PaginationRequest, lsaType: LsaType, externalIds: Collection): PageResponse { + fun findSharingStates(paginationRequest: PaginationRequest, lsaType: LsaType?, externalIds: Collection?): PageResponse { - val pageable = PageRequest.of(paginationRequest.page, paginationRequest.size) - val page = this.stateRepository.findByExternalIdInAndLsaType(externalIds, lsaType, pageable) + val spec = Specification.allOf(byLsaType(lsaType), byExternalIdsIn(externalIds)) + val pageRequest = PageRequest.of(paginationRequest.page, paginationRequest.size) + val page = stateRepository.findAll(spec, pageRequest) return page.toDto(page.content.map { SharingStateDto( From 9c031ee4a37abdc2daba40c75cf6ed6a82ea687f Mon Sep 17 00:00:00 2001 From: rschneider <97682836+rainer-exxcellent@users.noreply.github.com> Date: Wed, 24 May 2023 06:37:11 +0200 Subject: [PATCH 8/8] feat(sharing state): #163 add testcases for optional filtering --- .../controller/SharingStateControllerIT.kt | 66 +++++++++++++++---- 1 file changed, 53 insertions(+), 13 deletions(-) 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 647512b06..abe53016e 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 @@ -56,19 +56,22 @@ class SharingStateControllerIT @Autowired constructor( val stateAddress = insertSharingStateSuccess(LsaType.Address, externalId = "exIdAddress") val stateSite = insertSharingStateSuccess(LsaType.Site, externalId = "exIdSite") - insertSharingStateSuccess(LsaType.LegalEntity, externalId = "exIdEntity1") + val stateLegalEntity1 = insertSharingStateSuccess(LsaType.LegalEntity, externalId = "exIdEntity1") val stateLegalEntity2 = insertSharingStateSuccess(LsaType.LegalEntity, externalId = "exIdEntity2") + insertSharingStateSuccess(LsaType.Address, externalId = "exIdMultiple") + insertSharingStateSuccess(LsaType.Site, externalId = "exIdMultiple") + insertSharingStateSuccess(LsaType.LegalEntity, externalId = "exIdMultiple") - val searchAddress = readSharingStates(LsaType.Address, "exIdAddress") - assertThat(searchAddress).hasSize(1) - assertThat(searchAddress.first()).isEqualTo(stateAddress) + val searchAddressById = readSharingStates(LsaType.Address, "exIdAddress") + assertThat(searchAddressById).hasSize(1) + assertThat(searchAddressById.first()).isEqualTo(stateAddress) - val searchSites = readSharingStates(LsaType.Site, "exIdSite") - assertThat(searchSites).hasSize(1) - assertThat(searchSites.first()).isEqualTo(stateSite) + val searchSitesById = readSharingStates(LsaType.Site, "exIdSite") + assertThat(searchSitesById).hasSize(1) + assertThat(searchSitesById.first()).isEqualTo(stateSite) - val searchWrongId = readSharingStates(LsaType.Address, "exIdEntity") - assertThat(searchWrongId).hasSize(0) + val searchAddressWrongId = readSharingStates(LsaType.Address, "exIdEntity") + assertThat(searchAddressWrongId).hasSize(0) val searchEntityMultiple = readSharingStates(LsaType.LegalEntity, "exIdEntity1", "exIdEntity2") assertThat(searchEntityMultiple).hasSize(2) @@ -77,6 +80,19 @@ class SharingStateControllerIT @Autowired constructor( assertThat(searchEntitySingle).hasSize(1) assertThat(searchEntitySingle.first()).isEqualTo(stateLegalEntity2) + val searchAll = readSharingStates(null) + assertThat(searchAll).hasSize(7) + + val searchEntityAllLegalEntities = readSharingStates(LsaType.LegalEntity) + assertThat(searchEntityAllLegalEntities).hasSize(3) + assertThat(searchEntityAllLegalEntities).extracting(SharingStateDto::externalId.name) + .contains(stateLegalEntity1.externalId, stateLegalEntity2.externalId, "exIdMultiple") + + val searchAllWithSameId = readSharingStates(null, "exIdMultiple") + assertThat(searchAllWithSameId).hasSize(3) + assertThat(searchAllWithSameId).extracting(SharingStateDto::externalId.name) + .containsOnly("exIdMultiple") + } @Test @@ -89,7 +105,6 @@ class SharingStateControllerIT @Autowired constructor( val searchAddress = readSharingStates(LsaType.Address, "exIdAddress1") assertThat(searchAddress).hasSize(1) assertThat(searchAddress.first()).isEqualTo(stateAddress1) - } @Test @@ -118,18 +133,43 @@ class SharingStateControllerIT @Autowired constructor( assertThat(readUpdatedAddress.first()).isEqualTo(updatedAddress1) } + @Test + fun `insert and update states with sharingProcessStarted`() { + + val startTime = LocalDateTime.now().withNano(0) + val stateAddress1 = insertSharingStateSuccess( + lsaType = LsaType.Address, externalId = "exIdAddress1", + sharingProcessStarted = startTime + ) + + val readInsertedAddress = readSharingStates(LsaType.Address, "exIdAddress1") + assertThat(readInsertedAddress.first().sharingProcessStarted).isEqualTo(startTime) + + val updatedWithEmpyStarted = stateAddress1.copy( + sharingStateType = SharingStateType.Error, + sharingProcessStarted = null, + sharingErrorMessage = "Changed", + ) + gateClient.sharingState().upsertSharingState(updatedWithEmpyStarted) + + val readUpdatedAddress = readSharingStates(LsaType.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") + + } /** * Insert Sharing State only with required fields filled */ - fun insertSharingStateSuccess(lsaType: LsaType, externalId: String): SharingStateDto { + fun insertSharingStateSuccess(lsaType: LsaType, externalId: String, sharingProcessStarted: LocalDateTime? = null): SharingStateDto { val newState = SharingStateDto( lsaType = lsaType, externalId = externalId, sharingStateType = SharingStateType.Success, sharingErrorCode = null, - sharingProcessStarted = null, + sharingProcessStarted = sharingProcessStarted, sharingErrorMessage = null, bpn = null ) @@ -155,7 +195,7 @@ class SharingStateControllerIT @Autowired constructor( return newState } - fun readSharingStates(lsaType: LsaType, vararg externalIds: String): Collection { + fun readSharingStates(lsaType: LsaType?, vararg externalIds: String): Collection { return gateClient.sharingState().getSharingStates(PaginationRequest(), lsaType, externalIds.asList()).content }