Skip to content

Commit

Permalink
feat(Gate): generic business partners can now be shared on demand
Browse files Browse the repository at this point in the history
- created API endpoint to set sharing state to ready
- created scheduled function to poll ready business partners and share them to the orchestrator
- moved all scheduled functions to their own golden record task service
- aligned the configuration properties and made them less error prone
  • Loading branch information
nicoprow committed Dec 29, 2023
1 parent 4fce272 commit dcd7d10
Show file tree
Hide file tree
Showing 14 changed files with 540 additions and 131 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ import jakarta.validation.Valid
import org.eclipse.tractusx.bpdm.common.dto.BusinessPartnerType
import org.eclipse.tractusx.bpdm.common.dto.PageDto
import org.eclipse.tractusx.bpdm.common.dto.PaginationRequest
import org.eclipse.tractusx.bpdm.gate.api.model.request.PostSharingStateReadyRequest
import org.eclipse.tractusx.bpdm.gate.api.model.response.SharingStateDto
import org.springdoc.core.annotations.ParameterObject
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.*
import org.springframework.web.service.annotation.GetExchange
import org.springframework.web.service.annotation.HttpExchange
import org.springframework.web.service.annotation.PostExchange
import org.springframework.web.service.annotation.PutExchange

@RequestMapping("/api/catena/sharing-state", produces = [MediaType.APPLICATION_JSON_VALUE])
Expand All @@ -56,6 +58,22 @@ interface GateSharingStateApi {
@Parameter(description = "External IDs") @RequestParam(required = false) externalIds: Collection<String>?
): PageDto<SharingStateDto>

@Operation(
summary = "Sets the given business partners into ready to be shared state",
description = "The business partners to set the ready state for are identified by their external-id. Only business partners in an initial or error state can be set to ready. If any given business partner could not be set into ready state for any reason (for example, it has not been found or it is in the wrong state) the whole request fails (all or nothing approach)."
)
@ApiResponses(
value = [
ApiResponse(responseCode = "204", description = "All business partners put in ready to be shared state"),
ApiResponse(responseCode = "404", description = "Business partners can't be put into ready state (e.g. external-ID not found, wrong sharing state)")
]
)
@PostMapping
@PostExchange
fun postSharingStateReady(@RequestBody request: PostSharingStateReadyRequest)



@Operation(
summary = "Creates or updates a sharing state of a business partner",
deprecated = true
Expand All @@ -69,4 +87,6 @@ interface GateSharingStateApi {
@PutMapping
@PutExchange
fun upsertSharingState(@RequestBody request: SharingStateDto)


}
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ enum class SharingStateType {
Pending,
Success,
Error,
Initial
Initial,
Ready
}
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.api.model.request

import io.swagger.v3.oas.annotations.media.Schema

@Schema(description = "Request for setting business partners into ready to be shared to golden record state")
data class PostSharingStateReadyRequest(
val externalIds: List<String>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*******************************************************************************
* 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.config

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.stereotype.Component

@Component
@ConfigurationProperties(prefix = "bpdm.tasks")
data class GoldenRecordTaskConfigProperties(
val creation: CreationProperties = CreationProperties(),
val check: TaskProcessProperties = TaskProcessProperties()
) {
data class CreationProperties(
val fromSharingMember: TaskProcessProperties = TaskProcessProperties(),
val fromPool: TaskProcessProperties = TaskProcessProperties()
)

data class TaskProcessProperties(
var batchSize: Int = 100,
var cron: String = "-",
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.eclipse.tractusx.bpdm.common.dto.BusinessPartnerType
import org.eclipse.tractusx.bpdm.common.dto.PageDto
import org.eclipse.tractusx.bpdm.common.dto.PaginationRequest
import org.eclipse.tractusx.bpdm.gate.api.GateSharingStateApi
import org.eclipse.tractusx.bpdm.gate.api.model.request.PostSharingStateReadyRequest
import org.eclipse.tractusx.bpdm.gate.api.model.response.SharingStateDto
import org.eclipse.tractusx.bpdm.gate.service.SharingStateService
import org.springframework.security.access.prepost.PreAuthorize
Expand All @@ -44,6 +45,10 @@ class SharingStateController(
return sharingStateService.findSharingStates(paginationRequest, businessPartnerType, externalIds)
}

override fun postSharingStateReady(request: PostSharingStateReadyRequest) {
sharingStateService.setReady(request.externalIds)
}

@PreAuthorize("hasAuthority(@gateSecurityConfigProperties.getChangeCompanyOutputDataAsRole())")
override fun upsertSharingState(request: SharingStateDto) {
logger.info { "upsertSharingState() called with $request" }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (c) 2021,2023 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/

package org.eclipse.tractusx.bpdm.gate.exception

import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateType
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.ResponseStatus

@ResponseStatus(HttpStatus.BAD_REQUEST)
class BpdmInvalidStateException(
invalidStates: List<InvalidState>
) : RuntimeException("The following business partners are in an invalid state: ${invalidStates.joinToString { it.toString() }}") {
data class InvalidState(
val externalId: String,
val stateType: SharingStateType
)
}
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.exception

import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.ResponseStatus

@ResponseStatus(HttpStatus.BAD_REQUEST)
class BpdmMissingPartnerException(
externalIds: List<String>
) : RuntimeException("Business partners with the following external ids not found: ${externalIds.joinToString()}")
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ package org.eclipse.tractusx.bpdm.gate.repository
import org.eclipse.tractusx.bpdm.common.dto.BusinessPartnerType
import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateType
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
Expand Down Expand Up @@ -55,5 +57,7 @@ interface SharingStateRepository : PagingAndSortingRepository<SharingState, Long

fun findBySharingStateType(sharingStateType: SharingStateType): Set<SharingState>

fun findBySharingStateTypeAndTaskIdNotNull(sharingStateType: SharingStateType): Set<SharingState>
fun findBySharingStateType(sharingStateType: SharingStateType, pageable: Pageable): Page<SharingState>

fun findBySharingStateTypeAndTaskIdNotNull(sharingStateType: SharingStateType, pageable: Pageable): Page<SharingState>
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,35 +22,32 @@ package org.eclipse.tractusx.bpdm.gate.service
import mu.KotlinLogging
import org.eclipse.tractusx.bpdm.common.dto.BusinessPartnerType
import org.eclipse.tractusx.bpdm.common.dto.PageDto
import org.eclipse.tractusx.bpdm.common.dto.PaginationRequest
import org.eclipse.tractusx.bpdm.common.model.StageType
import org.eclipse.tractusx.bpdm.common.service.toPageDto
import org.eclipse.tractusx.bpdm.common.util.copyAndSync
import org.eclipse.tractusx.bpdm.common.util.replace
import org.eclipse.tractusx.bpdm.gate.api.exception.BusinessPartnerSharingError
import org.eclipse.tractusx.bpdm.gate.api.model.ChangelogType
import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateType
import org.eclipse.tractusx.bpdm.gate.api.model.request.BusinessPartnerInputRequest
import org.eclipse.tractusx.bpdm.gate.api.model.response.BusinessPartnerInputDto
import org.eclipse.tractusx.bpdm.gate.api.model.response.BusinessPartnerOutputDto
import org.eclipse.tractusx.bpdm.gate.config.PoolConfigProperties
import org.eclipse.tractusx.bpdm.gate.entity.ChangelogEntry
import org.eclipse.tractusx.bpdm.gate.entity.SyncType
import org.eclipse.tractusx.bpdm.gate.entity.generic.*
import org.eclipse.tractusx.bpdm.gate.exception.BpdmMissingStageException
import org.eclipse.tractusx.bpdm.gate.repository.ChangelogRepository
import org.eclipse.tractusx.bpdm.gate.repository.SharingStateRepository
import org.eclipse.tractusx.bpdm.gate.repository.generic.BusinessPartnerRepository
import org.eclipse.tractusx.bpdm.pool.api.client.PoolApiClient
import org.eclipse.tractusx.bpdm.pool.api.model.request.ChangelogSearchRequest
import org.eclipse.tractusx.orchestrator.api.client.OrchestrationApiClient
import org.eclipse.tractusx.orchestrator.api.model.*
import org.eclipse.tractusx.orchestrator.api.model.BusinessPartnerGenericDto
import org.eclipse.tractusx.orchestrator.api.model.TaskCreateRequest
import org.eclipse.tractusx.orchestrator.api.model.TaskCreateResponse
import org.eclipse.tractusx.orchestrator.api.model.TaskMode
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageRequest
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.eclipse.tractusx.bpdm.pool.api.model.ChangelogType as PoolChangeLogType

@Service
class BusinessPartnerService(
Expand Down Expand Up @@ -97,25 +94,12 @@ class BusinessPartnerService(
saveChangelog(resolutionResults)

val partners = resolutionResults.map { it.businessPartner }
val orchestratorBusinessPartnersDto = resolutionResults.map { orchestratorMappings.toBusinessPartnerGenericDto(it.businessPartner) }

val taskIds = createGoldenRecordTasks(TaskMode.UpdateFromSharingMember, orchestratorBusinessPartnersDto).createdTasks.map { it.taskId }

val pendingRequests = partners.zip(taskIds)
.map { (partner, taskId) ->
SharingStateService.PendingRequest(
SharingStateService.SharingStateIdentifierDto(
partner.externalId,
BusinessPartnerType.GENERIC
), taskId
)
}
sharingStateService.setPending(pendingRequests)
sharingStateService.setInitial(partners.map { SharingStateService.SharingStateIdentifierDto(it.externalId, BusinessPartnerType.GENERIC) })

return businessPartnerRepository.saveAll(partners)
}

private fun upsertBusinessPartnersOutputFromCandidates(entityCandidates: List<BusinessPartner>): List<BusinessPartner> {
fun upsertBusinessPartnersOutputFromCandidates(entityCandidates: List<BusinessPartner>): List<BusinessPartner> {
val externalIds = entityCandidates.map { it.externalId }
assertInputStageExists(externalIds)

Expand Down Expand Up @@ -305,90 +289,4 @@ class BusinessPartnerService(
)
}

@Scheduled(cron = "\${bpdm.cleaningService.pollingCron:-}", zone = "UTC")
@Transactional
fun finishCleaningTask() {
val sharingStates = sharingStateRepository.findBySharingStateTypeAndTaskIdNotNull(SharingStateType.Pending)
val tasks = orchestrationApiClient.goldenRecordTasks.searchTaskStates(TaskStateRequest(sharingStates.map { it.taskId!! })).tasks

val sharingStateMap = sharingStates.associateBy { it.taskId }

val taskStatesByResult = tasks
.map { Pair(it, sharingStateMap[it.taskId]!!) }
.groupBy { (task, _) -> task.processingState.resultState }

val sharingStatesWithoutTasks = sharingStates.filter { it.taskId !in tasks.map { task -> task.taskId } }

val businessPartnersToUpsert = taskStatesByResult[ResultState.Success]?.map { (task, sharingState) ->
orchestratorMappings.toBusinessPartner(task.businessPartnerResult!!, sharingState.externalId)
} ?: emptyList()
upsertBusinessPartnersOutputFromCandidates(businessPartnersToUpsert)

val errorRequests = (taskStatesByResult[ResultState.Error]?.map { (task, sharingState) ->
SharingStateService.ErrorRequest(
SharingStateService.SharingStateIdentifierDto(sharingState.externalId, sharingState.businessPartnerType),
BusinessPartnerSharingError.SharingProcessError,
if (task.processingState.errors.isNotEmpty()) task.processingState.errors.joinToString(" // ") { it.description } else null
)
} ?: emptyList()).toMutableList()

errorRequests.addAll(sharingStatesWithoutTasks.map { sharingState ->
SharingStateService.ErrorRequest(
SharingStateService.SharingStateIdentifierDto(sharingState.externalId, sharingState.businessPartnerType),
BusinessPartnerSharingError.MissingTaskID,
errorMessage = "Missing Task in Orchestrator"
)
})

sharingStateService.setError(errorRequests)

}

@Scheduled(cron = "\${cleaningService.pollingCron:-}", zone = "UTC")
@Transactional
fun requestCleaningTaskForGateOutputIfPoolBpnHasChanges() {

val poolPaginationRequest = PaginationRequest(0, poolConfigProperties.searchChangelogPageSize)

val syncRecord = syncRecordService.getOrCreateRecord(SyncType.POOL_TO_GATE_OUTPUT)

val changelogSearchRequest = ChangelogSearchRequest(syncRecord.finishedAt)


val poolChangelogEntries = poolClient.changelogs.getChangelogEntries(changelogSearchRequest, poolPaginationRequest)
val poolUpdatedEntries = poolChangelogEntries.content.filter { it.changelogType == PoolChangeLogType.UPDATE }


val bpnA =
poolUpdatedEntries.filter { it.businessPartnerType == BusinessPartnerType.ADDRESS }.map { it.bpn }
val bpnL = poolUpdatedEntries.filter { it.businessPartnerType == BusinessPartnerType.LEGAL_ENTITY }
.map { it.bpn }
val bpnS =
poolUpdatedEntries.filter { it.businessPartnerType == BusinessPartnerType.SITE }.map { it.bpn }

val gateOutputEntries = businessPartnerRepository.findByStageAndBpnLInOrBpnSInOrBpnAIn(StageType.Output, bpnL, bpnS, bpnA)

val businessPartnerGenericDtoList = gateOutputEntries.map { bp ->
orchestratorMappings.toBusinessPartnerGenericDto(bp)
}

val taskIds = createGoldenRecordTasks(TaskMode.UpdateFromPool, businessPartnerGenericDtoList).createdTasks.map { it.taskId }

val pendingRequests = gateOutputEntries.zip(taskIds)
.map { (partner, taskId) ->
SharingStateService.PendingRequest(
SharingStateService.SharingStateIdentifierDto(
partner.externalId,
partner.parentType ?: BusinessPartnerType.GENERIC
), taskId
)
}
sharingStateService.setPending(pendingRequests)

if (poolUpdatedEntries.isNotEmpty()) {
syncRecordService.setSynchronizationStart(SyncType.POOL_TO_GATE_OUTPUT)
syncRecordService.setSynchronizationSuccess(SyncType.POOL_TO_GATE_OUTPUT, poolUpdatedEntries.last().timestamp)
}
}

}
Loading

0 comments on commit dcd7d10

Please sign in to comment.