Skip to content

Commit

Permalink
Merge pull request #171 from catenax-ng/feat/persistence_query_input
Browse files Browse the repository at this point in the history
Feat - Fetch of input data from the persistence layer (Logistic Address)
  • Loading branch information
nicoprow authored May 30, 2023
2 parents a0effc1 + e804d51 commit a90fad6
Show file tree
Hide file tree
Showing 10 changed files with 317 additions and 722 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,11 @@ class GateQueryService(
do {
val pageResponse = gateClient.addresses().getAddressesByExternalIds(
externalIds = externalIds,
paginationRequest = PaginationStartAfterRequest(pageStartAfter, bridgeConfigProperties.queryPageSize)
paginationRequest = PaginationRequest(0, bridgeConfigProperties.queryPageSize)
)
pageStartAfter = pageResponse.nextStartAfter
//pageStartAfter = pageResponse.nextStartAfter
validContent.addAll(pageResponse.content)
invalidEntries += pageResponse.invalidEntries
invalidEntries += 0 //TODO Needs to be changed according to the removal of SaaS
} while (pageStartAfter != null)

logger.info { "Gate returned ${validContent.size} valid addresses, $invalidEntries were invalid" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import jakarta.validation.Valid
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.AddressGateInputRequest
import org.eclipse.tractusx.bpdm.gate.api.model.AddressGateInputResponse
import org.eclipse.tractusx.bpdm.gate.api.model.AddressGateOutput
import org.eclipse.tractusx.bpdm.gate.api.model.request.PaginationStartAfterRequest
import org.eclipse.tractusx.bpdm.gate.api.model.response.PageOutputResponse
import org.eclipse.tractusx.bpdm.gate.api.model.response.PageStartAfterResponse
import org.eclipse.tractusx.bpdm.gate.api.model.response.ValidationResponse
import org.springdoc.core.annotations.ParameterObject
import org.springframework.http.MediaType
Expand Down Expand Up @@ -92,9 +93,9 @@ interface GateAddressApi {
@PostMapping("/input/addresses/search")
@PostExchange("/input/addresses/search")
fun getAddressesByExternalIds(
@ParameterObject @Valid paginationRequest: PaginationStartAfterRequest,
@ParameterObject @Valid paginationRequest: PaginationRequest,
@RequestBody externalIds: Collection<String>
): PageStartAfterResponse<AddressGateInputResponse>
): PageResponse<AddressGateInputResponse>


@Operation(
Expand All @@ -109,7 +110,7 @@ interface GateAddressApi {
)
@GetMapping("/input/addresses")
@GetExchange("/input/addresses")
fun getAddresses(@ParameterObject @Valid paginationRequest: PaginationStartAfterRequest): PageStartAfterResponse<AddressGateInputResponse>
fun getAddresses(@ParameterObject @Valid paginationRequest: PaginationRequest): PageResponse<AddressGateInputResponse>

@Operation(
summary = "Get page of addresses",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@

package org.eclipse.tractusx.bpdm.gate.controller

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.GateAddressApi
import org.eclipse.tractusx.bpdm.gate.api.model.AddressGateInputRequest
import org.eclipse.tractusx.bpdm.gate.api.model.AddressGateInputResponse
import org.eclipse.tractusx.bpdm.gate.api.model.AddressGateOutput
import org.eclipse.tractusx.bpdm.gate.api.model.request.PaginationStartAfterRequest
import org.eclipse.tractusx.bpdm.gate.api.model.response.PageOutputResponse
import org.eclipse.tractusx.bpdm.gate.api.model.response.PageStartAfterResponse
import org.eclipse.tractusx.bpdm.gate.api.model.response.ValidationResponse
import org.eclipse.tractusx.bpdm.gate.config.ApiConfigProperties
import org.eclipse.tractusx.bpdm.gate.containsDuplicates
Expand Down Expand Up @@ -60,14 +61,14 @@ class AddressController(
}

override fun getAddressesByExternalIds(
paginationRequest: PaginationStartAfterRequest,
paginationRequest: PaginationRequest,
externalIds: Collection<String>
): PageStartAfterResponse<AddressGateInputResponse> {
return addressService.getAddresses(limit = paginationRequest.limit, startAfter = paginationRequest.startAfter, externalIds = externalIds)
): PageResponse<AddressGateInputResponse> {
return addressService.getAddresses(page = paginationRequest.page, size = paginationRequest.size, externalIds = externalIds)
}

override fun getAddresses(paginationRequest: PaginationStartAfterRequest): PageStartAfterResponse<AddressGateInputResponse> {
return addressService.getAddresses(paginationRequest.limit, paginationRequest.startAfter)
override fun getAddresses(paginationRequest: PaginationRequest): PageResponse<AddressGateInputResponse> {
return addressService.getAddresses(page = paginationRequest.page, size = paginationRequest.size)
}

override fun getAddressesOutput(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@
package org.eclipse.tractusx.bpdm.gate.repository

import org.eclipse.tractusx.bpdm.gate.entity.LogisticAddress
import org.springframework.data.jpa.repository.JpaRepository
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 GateAddressRepository : JpaRepository<LogisticAddress, Long>, CrudRepository<LogisticAddress, Long> {
interface GateAddressRepository : PagingAndSortingRepository<LogisticAddress, Long>, CrudRepository<LogisticAddress, Long> {

fun findByExternalIdIn(externalId: Collection<String>): Set<LogisticAddress>

fun findByExternalId(externalId: String): LogisticAddress?

fun findByExternalIdIn(externalId: Collection<String>?, pageable: Pageable): Page<LogisticAddress>

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,5 @@ import org.springframework.data.repository.CrudRepository
interface LegalEntityRepository : JpaRepository<LegalEntity, Long>, CrudRepository<LegalEntity, Long> {

fun findDistinctByExternalIdIn(externalId: Collection<String>): Set<LegalEntity>

fun findByExternalId(externalId: String): LegalEntity?
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,22 @@ package org.eclipse.tractusx.bpdm.gate.service

import mu.KotlinLogging
import org.eclipse.tractusx.bpdm.common.dto.response.LogisticAddressResponse
import org.eclipse.tractusx.bpdm.common.dto.response.PageResponse
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.PagedResponseSaas
import org.eclipse.tractusx.bpdm.common.exception.BpdmNotFoundException
import org.eclipse.tractusx.bpdm.gate.api.model.AddressGateInputRequest
import org.eclipse.tractusx.bpdm.gate.api.model.AddressGateInputResponse
import org.eclipse.tractusx.bpdm.gate.api.model.AddressGateOutput
import org.eclipse.tractusx.bpdm.gate.api.model.response.LsaType
import org.eclipse.tractusx.bpdm.gate.api.model.response.OptionalLsaType
import org.eclipse.tractusx.bpdm.gate.api.model.response.PageOutputResponse
import org.eclipse.tractusx.bpdm.gate.api.model.response.PageStartAfterResponse
import org.eclipse.tractusx.bpdm.gate.config.BpnConfigProperties
import org.eclipse.tractusx.bpdm.gate.entity.ChangelogEntry
import org.eclipse.tractusx.bpdm.gate.exception.SaasInvalidRecordException
import org.eclipse.tractusx.bpdm.gate.entity.LogisticAddress
import org.eclipse.tractusx.bpdm.gate.exception.SaasNonexistentParentException
import org.eclipse.tractusx.bpdm.gate.repository.ChangelogRepository
import org.eclipse.tractusx.bpdm.gate.repository.GateAddressRepository
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageRequest
import org.springframework.stereotype.Service

@Service
Expand All @@ -49,71 +49,40 @@ class AddressService(
private val bpnConfigProperties: BpnConfigProperties,
private val typeMatchingService: TypeMatchingService,
private val changelogRepository: ChangelogRepository,
private val addressPersistenceService: AddressPersistenceService
private val addressPersistenceService: AddressPersistenceService,
private val addressRepository: GateAddressRepository
) {
private val logger = KotlinLogging.logger { }

fun getAddresses(limit: Int, startAfter: String?, externalIds: Collection<String>? = null): PageStartAfterResponse<AddressGateInputResponse> {
val addressesPage = saasClient.getAddresses(limit, startAfter, externalIds)
fun getAddresses(page: Int, size: Int, externalIds: Collection<String>? = null): PageResponse<AddressGateInputResponse> {

val addressGateInputResponse = toValidAddresses(addressesPage)
val logisticAddressPage = if (externalIds != null) {
addressRepository.findByExternalIdIn(externalIds, PageRequest.of(page, size))
} else {
addressRepository.findAll(PageRequest.of(page, size))
}

return PageStartAfterResponse(
total = addressesPage.total,
nextStartAfter = addressesPage.nextStartAfter,
content = addressGateInputResponse,
invalidEntries = addressesPage.values.size - addressGateInputResponse.size
return PageResponse(
page = page,
totalElements = logisticAddressPage.totalElements,
totalPages = logisticAddressPage.totalPages,
contentSize = logisticAddressPage.content.size,
content = toValidLogisticAddresses(logisticAddressPage),
)
}

private fun toValidAddresses(addressesPage: PagedResponseSaas<BusinessPartnerSaas>): List<AddressGateInputResponse> {
val validEntries = addressesPage.values
.filter { validateAddressBusinessPartner(it) }

val addressesWithParent = validEntries.map {
Pair(it, inputSaasMappingService.toParentLegalEntityExternalId(it)!!)
}

val parents = if (addressesWithParent.isNotEmpty())
saasClient.getBusinessPartners(
externalIds = addressesWithParent.map { (_, parentId) -> parentId }
).values
else
emptyList()

val (legalEntityParents, siteParents) = typeMatchingService.partitionIntoParentTypes(parents)
val legalEntityParentIds = legalEntityParents.mapNotNull { it.externalId }.toHashSet()
val siteParentIds = siteParents.mapNotNull { it.externalId }.toHashSet()

return addressesWithParent.mapNotNull { (address, parentId) ->
try {
when {
legalEntityParentIds.contains(parentId) ->
inputSaasMappingService.toInputAddress(address, parentId, null)

siteParentIds.contains(parentId) ->
inputSaasMappingService.toInputAddress(address, null, parentId)

else -> {
logger.warn { "Could not fetch parent for SaaS address record with ID ${address.id}" }
null
}
}

} catch (e: RuntimeException) {
logger.warn { "SaaS address record with ID ${address.id} will be ignored: ${e.message}" }
null
}
private fun toValidLogisticAddresses(logisticAddressPage: Page<LogisticAddress>): List<AddressGateInputResponse> {
return logisticAddressPage.content.map { logisticAddress ->
logisticAddress.toAddressGateInputResponse(logisticAddress)
}
}

fun getAddressByExternalId(externalId: String): AddressGateInputResponse {
val fetchResponse = saasClient.getBusinessPartner(externalId)

when (fetchResponse.status) {
FetchResponse.Status.OK -> return toValidAddressInput(fetchResponse.businessPartner!!)
FetchResponse.Status.NOT_FOUND -> throw BpdmNotFoundException("Address", externalId)
}
val logisticAddress = addressRepository.findByExternalId(externalId) ?: throw BpdmNotFoundException("Logistic Address", externalId)

return logisticAddress.toAddressGateInputResponse(logisticAddress)

}

/**
Expand Down Expand Up @@ -175,18 +144,11 @@ class AddressService(
*/
fun upsertAddresses(addresses: Collection<AddressGateInputRequest>) {

val addressesSaas = toSaasModels(addresses)
saasClient.upsertAddresses(addressesSaas)

// create changelog entry if all goes well from saasClient
addresses.forEach { address ->
changelogRepository.save(ChangelogEntry(address.externalId, LsaType.Address))
}

deleteParentRelationsOfAddresses(addresses)

upsertParentRelations(addresses)

addressPersistenceService.persistAddressBP(addresses)
}

Expand All @@ -200,37 +162,6 @@ class AddressService(
return addresses.map { toSaasModel(it, parentLegalEntitiesByExternalId[it.legalEntityExternalId], parentSitesByExternalId[it.siteExternalId]) }
}

private fun upsertParentRelations(addresses: Collection<AddressGateInputRequest>) {
val legalEntityRelations = toLegalEntityParentRelations(addresses)
val siteRelations = toSiteParentRelations(addresses)
saasClient.upsertAddressRelations(legalEntityRelations, siteRelations)
}

private fun deleteParentRelationsOfAddresses(addresses: Collection<AddressGateInputRequest>) {
val addressesPage = saasClient.getAddresses(externalIds = addresses.map { it.externalId })
saasClient.deleteParentRelations(addressesPage.values)
}

private fun toSiteParentRelations(addresses: Collection<AddressGateInputRequest>) =
addresses.filter {
it.siteExternalId != null
}.map {
SaasClient.AddressSiteRelation(
addressExternalId = it.externalId,
siteExternalId = it.siteExternalId!!
)
}.toList()

private fun toLegalEntityParentRelations(addresses: Collection<AddressGateInputRequest>) =
addresses.filter {
it.legalEntityExternalId != null
}.map {
SaasClient.AddressLegalEntityRelation(
addressExternalId = it.externalId,
legalEntityExternalId = it.legalEntityExternalId!!
)
}.toList()

private fun getParentSites(addresses: Collection<AddressGateInputRequest>): Map<String, BusinessPartnerSaas> {
val parentSiteExternalIds = addresses.mapNotNull { it.siteExternalId }.distinct().toList()
var parentSitesByExternalId: Map<String, BusinessPartnerSaas> = HashMap()
Expand Down Expand Up @@ -270,42 +201,4 @@ class AddressService(
return addressSaas.copy(identifiers = addressSaas.identifiers.plus(parentIdentifiersWithoutBpn), names = parentNames)
}

private fun toValidAddressInput(partner: BusinessPartnerSaas): AddressGateInputResponse {
if (!validateAddressBusinessPartner(partner)) {
throw SaasInvalidRecordException(partner.id)
}

val parentId = inputSaasMappingService.toParentLegalEntityExternalId(partner)
val parentType = parentId?.let { saasClient.getBusinessPartner(it).businessPartner }?.let { typeMatchingService.determineType(it) }

return when (parentType) {
OptionalLsaType.LegalEntity -> inputSaasMappingService.toInputAddress(partner, parentId, null)
OptionalLsaType.Site -> inputSaasMappingService.toInputAddress(partner, null, parentId)
else -> throw SaasInvalidRecordException(parentId)
}
}

private fun validateAddressBusinessPartner(partner: BusinessPartnerSaas): Boolean {
val logMessageStart = "SaaS business partner for address with ${if (partner.id != null) "ID " + partner.id else "external id " + partner.externalId}"

if (partner.addresses.size > 1) {
logger.warn { "$logMessageStart has multiple addresses" }
}
if (partner.addresses.isEmpty()) {
logger.warn { "$logMessageStart does not have an address" }
return false
}

val numParents = inputSaasMappingService.toParentLegalEntityExternalIds(partner).size
if (numParents > 1) {
logger.warn { "$logMessageStart has multiple parents." }
}

if (numParents == 0) {
logger.warn { "$logMessageStart does not have a parent legal entity or site." }
return false
}

return true
}
}
Loading

0 comments on commit a90fad6

Please sign in to comment.