Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/gate/sharing status response #68

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The format is based on Keep a Changelog (https://keepachangelog.com/en/1.0.0/),
### Fixed

- BPDM Gate: internal server error when invoking the POST business-partners/type-match endpoint
- BPDM Gate: output endpoints could miss returning any information for Business Partners in some cases

## [3.1.0] - 2023-03-08

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ interface ErrorCode
enum class BusinessPartnerOutputError : ErrorCode {
SharingProcessError,
SharingTimeout,
BpnNotInPool,
BpnNotInPool
}
Original file line number Diff line number Diff line change
@@ -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.model

import org.eclipse.tractusx.bpdm.common.dto.saas.BusinessPartnerSaas

data class BusinessPartnerSaasWithBpn(
val partner: BusinessPartnerSaas,
val bpn: String,
val externalId: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*******************************************************************************
* 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.model

import org.eclipse.tractusx.bpdm.common.dto.saas.BusinessPartnerSaas

data class BusinessPartnerSaasWithExternalId(
val partner: BusinessPartnerSaas,
val externalId: String
)
Original file line number Diff line number Diff line change
@@ -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 org.eclipse.tractusx.bpdm.gate.model

import org.eclipse.tractusx.bpdm.gate.dto.response.ErrorInfo
import org.eclipse.tractusx.bpdm.gate.exception.BusinessPartnerOutputError

data class SharingStatusEvaluationResult(
val validExternalIds: Collection<String>,
val pendingExternalIds: Collection<String>,
val errors: Collection<ErrorInfo<BusinessPartnerOutputError>>
)
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,14 @@ import mu.KotlinLogging
import org.eclipse.tractusx.bpdm.common.dto.response.AddressPartnerSearchResponse
import org.eclipse.tractusx.bpdm.common.dto.saas.BusinessPartnerSaas
import org.eclipse.tractusx.bpdm.common.dto.saas.FetchResponse
import org.eclipse.tractusx.bpdm.common.dto.saas.isError
import org.eclipse.tractusx.bpdm.common.exception.BpdmNotFoundException
import org.eclipse.tractusx.bpdm.gate.config.BpnConfigProperties
import org.eclipse.tractusx.bpdm.gate.dto.AddressGateInputRequest
import org.eclipse.tractusx.bpdm.gate.dto.AddressGateInputResponse
import org.eclipse.tractusx.bpdm.gate.dto.AddressGateOutput
import org.eclipse.tractusx.bpdm.gate.dto.response.ErrorInfo
import org.eclipse.tractusx.bpdm.gate.dto.response.LsaType
import org.eclipse.tractusx.bpdm.gate.dto.response.PageOutputResponse
import org.eclipse.tractusx.bpdm.gate.dto.response.PageStartAfterResponse
import org.eclipse.tractusx.bpdm.gate.exception.BusinessPartnerOutputError
import org.eclipse.tractusx.bpdm.gate.exception.SaasInvalidRecordException
import org.eclipse.tractusx.bpdm.gate.exception.SaasNonexistentParentException
import org.springframework.stereotype.Service
Expand Down Expand Up @@ -95,68 +92,40 @@ class AddressService(
* which is then used to fetch the data for the addresses from the bpdm pool.
*/
fun getAddressesOutput(externalIds: Collection<String>?, limit: Int, startAfter: String?): PageOutputResponse<AddressGateOutput> {
val augmentedPartnerResponse = saasClient.getAugmentedAddresses(limit = limit, startAfter = startAfter, externalIds = externalIds)
val augmentedPartnerWrapperCollection = augmentedPartnerResponse.values
val partnerResponse = saasClient.getAddresses(limit = limit, startAfter = startAfter, externalIds = externalIds)
val partners = partnerResponse.values

val bpnByExternalIdMap = outputSaasMappingService.buildBpnByExternalIdMap(augmentedPartnerWrapperCollection)
val partnersWithExternalId = outputSaasMappingService.mapWithExternalId(partners)
val augmentedPartnerResponse = saasClient.getAugmentedAddresses(externalIds = partnersWithExternalId.map { it.externalId })
val partnersWithLocalBpn = outputSaasMappingService.mapWithLocalBpn(partnersWithExternalId, augmentedPartnerResponse.values)

val bpnList = bpnByExternalIdMap.values.filterNotNull()
val addressesByBpnMap = poolClient.searchAddresses(bpnList).associateBy { it.address.bpn }
//Search entries in the pool with BPNs found in the local mirror
val bpnSet = partnersWithLocalBpn.map { it.bpn }.toSet()
val addressesByBpnMap = poolClient.searchAddresses(bpnSet).associateBy { it.address.bpn }

if (bpnList.size > addressesByBpnMap.size) {
logger.warn { "Requested ${bpnList.size} addresses from pool, but only ${addressesByBpnMap.size} were found." }
if (bpnSet.size > addressesByBpnMap.size) {
logger.warn { "Requested ${bpnSet.size} addresses from pool, but only ${addressesByBpnMap.size} were found." }
}

// We need the sharing status from BusinessPartnerSaas
val partnerResponse = saasClient.getAddresses(externalIds = bpnByExternalIdMap.keys)
val partnerByExternalIdMap = partnerResponse.values.associateBy { it.externalId }

// We sort all the entries in one of 3 buckets: valid content, errors or still pending
val validContent = mutableListOf<AddressGateOutput>()
val errors = mutableListOf<ErrorInfo<BusinessPartnerOutputError>>()
val pendingExternalIds = mutableListOf<String>()

for ((externalId, bpn) in bpnByExternalIdMap) {
// Business partner sharing
val partner = partnerByExternalIdMap[externalId]
val sharingStatus = partner?.metadata?.sharingStatus
val sharingStatusType = sharingStatus?.status

if (sharingStatusType == null || sharingStatusType.isError()) {
// ERROR: SharingProcessError
errors.add(
outputSaasMappingService.buildErrorInfoSharingProcessError(externalId, sharingStatus)
)
} else if (bpn != null) {
val address = addressesByBpnMap[bpn]
if (address != null) {
// OKAY: entry found in pool
validContent.add(
toAddressOutput(externalId, address)
)
} else {
// ERROR: BpnNotInPool
errors.add(
outputSaasMappingService.buildErrorInfoBpnNotInPool(externalId, bpn)
)
}
} else if (outputSaasMappingService.isSharingTimeoutReached(partner)) {
// ERROR: SharingTimeout
errors.add(
outputSaasMappingService.buildErrorInfoSharingTimeout(externalId, partner.lastModifiedAt)
)
} else {
pendingExternalIds.add(externalId)
}
val partnersWithPoolBpn = partnersWithLocalBpn.filter { addressesByBpnMap[it.bpn] != null }
val bpnByExternalIdMap = partnersWithPoolBpn.map { Pair(it.partner.externalId!!, it.bpn) }.toMap()

//Evaluate the sharing status of the legal entities
val sharingStatus = outputSaasMappingService.evaluateSharingStatus(partners, partnersWithLocalBpn, partnersWithPoolBpn)

val validAddresses = sharingStatus.validExternalIds.map { externalId ->
val bpn = bpnByExternalIdMap[externalId]!!
val address = addressesByBpnMap[bpn]!!
toAddressOutput(externalId, address)
}

return PageOutputResponse(
total = augmentedPartnerResponse.total,
nextStartAfter = augmentedPartnerResponse.nextStartAfter,
content = validContent,
invalidEntries = augmentedPartnerWrapperCollection.size - validContent.size, // difference between all entries from SaaS and valid content
pending = pendingExternalIds,
errors = errors,
total = partnerResponse.total,
nextStartAfter = partnerResponse.nextStartAfter,
content = validAddresses,
invalidEntries = partners.size - sharingStatus.validExternalIds.size, // difference between all entries from SaaS and valid content
pending = sharingStatus.pendingExternalIds,
errors = sharingStatus.errors,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,13 @@ import org.eclipse.tractusx.bpdm.common.dto.response.LegalAddressSearchResponse
import org.eclipse.tractusx.bpdm.common.dto.response.LegalEntityPartnerResponse
import org.eclipse.tractusx.bpdm.common.dto.saas.BusinessPartnerSaas
import org.eclipse.tractusx.bpdm.common.dto.saas.FetchResponse
import org.eclipse.tractusx.bpdm.common.dto.saas.isError
import org.eclipse.tractusx.bpdm.common.exception.BpdmMappingException
import org.eclipse.tractusx.bpdm.common.exception.BpdmNotFoundException
import org.eclipse.tractusx.bpdm.gate.dto.LegalEntityGateInputRequest
import org.eclipse.tractusx.bpdm.gate.dto.LegalEntityGateInputResponse
import org.eclipse.tractusx.bpdm.gate.dto.LegalEntityGateOutput
import org.eclipse.tractusx.bpdm.gate.dto.response.ErrorInfo
import org.eclipse.tractusx.bpdm.gate.dto.response.PageOutputResponse
import org.eclipse.tractusx.bpdm.gate.dto.response.PageStartAfterResponse
import org.eclipse.tractusx.bpdm.gate.exception.BusinessPartnerOutputError
import org.springframework.stereotype.Service

@Service
Expand Down Expand Up @@ -79,71 +76,46 @@ class LegalEntityService(
* which is then used to fetch the data for the legal entities from the bpdm pool.
*/
fun getLegalEntitiesOutput(externalIds: Collection<String>?, limit: Int, startAfter: String?): PageOutputResponse<LegalEntityGateOutput> {
val augmentedPartnerResponse = saasClient.getAugmentedLegalEntities(limit = limit, startAfter = startAfter, externalIds = externalIds)
val augmentedPartnerWrapperCollection = augmentedPartnerResponse.values
val partnerResponse = saasClient.getLegalEntities(limit = limit, startAfter = startAfter, externalIds = externalIds)
val partners = partnerResponse.values

val bpnByExternalIdMap = outputSaasMappingService.buildBpnByExternalIdMap(augmentedPartnerWrapperCollection)
val partnersWithExternalId = outputSaasMappingService.mapWithExternalId(partners)
val augmentedPartnerResponse = saasClient.getAugmentedLegalEntities(externalIds = partnersWithExternalId.map { it.externalId })
val partnersWithLocalBpn = outputSaasMappingService.mapWithLocalBpn(partnersWithExternalId, augmentedPartnerResponse.values)

val bpnList = bpnByExternalIdMap.values.filterNotNull()
val legalEntitiesByBpnMap = poolClient.searchLegalEntities(bpnList).associateBy { it.bpn }
val legalAddressesByBpnMap = poolClient.searchLegalAddresses(bpnList).associateBy { it.legalEntity }
//Search entries in the pool with BPNs found in the local mirror
val bpnSet = partnersWithLocalBpn.map { it.bpn }.toSet()
val legalEntitiesByBpnMap = poolClient.searchLegalEntities(bpnSet).associateBy { it.bpn }
val legalAddressesByBpnMap = poolClient.searchLegalAddresses(bpnSet).associateBy { it.legalEntity }

if (bpnList.size > legalEntitiesByBpnMap.size) {
logger.warn { "Requested ${bpnList.size} legal entities from pool, but only ${legalEntitiesByBpnMap.size} were found." }
if (bpnSet.size > legalEntitiesByBpnMap.size) {
logger.warn { "Requested ${bpnSet.size} legal entities from pool, but only ${legalEntitiesByBpnMap.size} were found." }
}
if (bpnList.size > legalAddressesByBpnMap.size) {
logger.warn { "Requested ${bpnList.size} legal addresses from pool, but only ${legalAddressesByBpnMap.size} were found." }
if (bpnSet.size > legalAddressesByBpnMap.size) {
logger.warn { "Requested ${bpnSet.size} legal addresses from pool, but only ${legalAddressesByBpnMap.size} were found." }
}

// We need the sharing status from BusinessPartnerSaas
val partnerResponse = saasClient.getLegalEntities(externalIds = bpnByExternalIdMap.keys)
val partnerByExternalIdMap = partnerResponse.values.associateBy { it.externalId }

// We sort all the entries in one of 3 buckets: valid content, errors or still pending
val validContent = mutableListOf<LegalEntityGateOutput>()
val errors = mutableListOf<ErrorInfo<BusinessPartnerOutputError>>()
val pendingExternalIds = mutableListOf<String>()

for ((externalId, bpn) in bpnByExternalIdMap) {
// Business partner sharing
val partner = partnerByExternalIdMap[externalId]
val sharingStatus = partner?.metadata?.sharingStatus
val sharingStatusType = sharingStatus?.status

if (sharingStatusType == null || sharingStatusType.isError()) {
// ERROR: SharingProcessError
errors.add(
outputSaasMappingService.buildErrorInfoSharingProcessError(externalId, sharingStatus))
} else if (bpn != null) {
val legalEntity = legalEntitiesByBpnMap[bpn]
val legalAddress = legalAddressesByBpnMap[bpn]
if (legalEntity != null && legalAddress != null) {
// OKAY: entry found in pool
validContent.add(
toLegalEntityOutput(externalId, legalEntity, legalAddress)
)
} else {
// ERROR: BpnNotInPool
errors.add(
outputSaasMappingService.buildErrorInfoBpnNotInPool(externalId, bpn))
}
} else if (outputSaasMappingService.isSharingTimeoutReached(partner)) {
// ERROR: SharingTimeout
errors.add(
outputSaasMappingService.buildErrorInfoSharingTimeout(externalId, partner.lastModifiedAt)
)
} else {
pendingExternalIds.add(externalId)
}
//Filter only legal entities which can be found with their legal address in the Pool under the given local BPN
val partnersWithPoolBpn = partnersWithLocalBpn.filter { legalEntitiesByBpnMap[it.bpn] != null && legalAddressesByBpnMap[it.bpn] != null }
val bpnByExternalIdMap = partnersWithPoolBpn.associate { Pair(it.externalId, it.bpn) }

//Evaluate the sharing status of the legal entities
val sharingStatus = outputSaasMappingService.evaluateSharingStatus(partners, partnersWithLocalBpn, partnersWithPoolBpn)

val validLegalEntities = sharingStatus.validExternalIds.map { externalId ->
val bpn = bpnByExternalIdMap[externalId]!!
val legalEntity = legalEntitiesByBpnMap[bpn]!!
val legalAddress = legalAddressesByBpnMap[bpn]!!
nicoprow marked this conversation as resolved.
Show resolved Hide resolved
toLegalEntityOutput(externalId, legalEntity, legalAddress)
}

return PageOutputResponse(
total = augmentedPartnerResponse.total,
nextStartAfter = augmentedPartnerResponse.nextStartAfter,
content = validContent,
invalidEntries = augmentedPartnerWrapperCollection.size - validContent.size, // difference between all entries from SaaS and valid content
pending = pendingExternalIds,
errors = errors,
total = partnerResponse.total,
nextStartAfter = partnerResponse.nextStartAfter,
content = validLegalEntities,
invalidEntries = partners.size - sharingStatus.validExternalIds.size, // difference between all entries from SaaS and valid content
pending = sharingStatus.pendingExternalIds,
errors = sharingStatus.errors,
)
}

Expand Down
Loading