Skip to content

Commit

Permalink
feat: add gate record ID for tasks to identify tasks for the same gat…
Browse files Browse the repository at this point in the history
…e records

- create gate record IDs on receiving task create request which do not already specify a record ID
- Gate now stores received record IDs to each sharing state, so that next time the record ID can be given to the orchestrator
- In the Orchestrator records are identified by both a private and a public record ID. The private record ID is for the Gates to use while the public record ID is sent to the cleaning services
  • Loading branch information
nicoprow committed Jul 9, 2024
1 parent 01364e0 commit a7466bb
Show file tree
Hide file tree
Showing 26 changed files with 305 additions and 45 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ For changes to the BPDM Helm charts please consult the [changelog](charts/bpdm/C
- BPDM Gate: GET endpoint to download the csv file template for business partner upload. (#700)
- Apps: Tax Jurisdiction Code to the physical address of a business partner (#955)
- BPDM Orchestrator: Tasks will now be persisted
- BPDM Orchestrator: Tasks now come with a gate record identifier. This makes it possible for cleaning services to match tasks for the same Gate record

### Changed:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import java.time.Instant
import java.util.*

@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
Expand Down Expand Up @@ -373,7 +374,7 @@ class CleaningServiceApiCallsTest @Autowired constructor(

// Helper method to create a sample TaskStepReservationResponse
private fun createSampleTaskStepReservationResponse(businessPartner: BusinessPartner): TaskStepReservationResponse {
return TaskStepReservationResponse(listOf(TaskStepReservationEntryDto(fixedTaskId, businessPartner)), Instant.MIN)
return TaskStepReservationResponse(listOf(TaskStepReservationEntryDto(fixedTaskId, UUID.randomUUID().toString(), businessPartner)), Instant.MIN)
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ 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 java.time.LocalDateTime
import java.util.*


@Entity
Expand All @@ -36,6 +37,9 @@ class SharingStateDb(
@Column(name = "tenant_bpnl", nullable = true)
var tenantBpnl: String? = null,

@Column(name = "orchestrator_record_id", nullable = true, unique = true)
var orchestratorRecordId: UUID?,

@Enumerated(EnumType.STRING)
@Column(name = "sharing_state_type", nullable = false)
var sharingStateType: SharingStateType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ class OrchestratorMappings(
) {
private val logger = KotlinLogging.logger { }

fun toCreateRequest(entity: BusinessPartnerDb): TaskCreateRequestEntry{
return TaskCreateRequestEntry(
recordId = entity.sharingState.orchestratorRecordId?.toString(),
businessPartner = toOrchestratorDto(entity)
)
}

fun toOrchestratorDto(entity: BusinessPartnerDb): BusinessPartner {
val postalAddress = toPostalAddress(entity)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ class SharingStateService(
SharingStateDb(
externalId,
sharingStateType = SharingStateType.Ready,
orchestratorRecordId = null,
sharingErrorCode = null,
sharingErrorMessage = null,
sharingProcessStarted = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ import org.eclipse.tractusx.bpdm.gate.config.GoldenRecordTaskConfigProperties
import org.eclipse.tractusx.bpdm.gate.repository.SharingStateRepository
import org.eclipse.tractusx.bpdm.gate.repository.generic.BusinessPartnerRepository
import org.eclipse.tractusx.orchestrator.api.client.OrchestrationApiClient
import org.eclipse.tractusx.orchestrator.api.model.BusinessPartner
import org.eclipse.tractusx.orchestrator.api.model.TaskClientStateDto
import org.eclipse.tractusx.orchestrator.api.model.TaskCreateRequest
import org.eclipse.tractusx.orchestrator.api.model.TaskCreateRequestEntry
import org.eclipse.tractusx.orchestrator.api.model.TaskMode
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.util.*

@Service
class TaskCreationService(
Expand All @@ -55,17 +56,18 @@ class TaskCreationService(
logger.debug { "Found ${foundStates.size} business partners in ready state" }

val foundPartners = businessPartnerRepository.findBySharingStateInAndStage(foundStates, StageType.Input)
val orchestratorBusinessPartnersDto = foundPartners.map { orchestratorMappings.toOrchestratorDto(it) }
val orchestratorBusinessPartnersDto = foundPartners.map { orchestratorMappings.toCreateRequest(it) }
val createdTasks = createGoldenRecordTasks(TaskMode.UpdateFromSharingMember, orchestratorBusinessPartnersDto)

foundPartners.zip(createdTasks).forEach { (partner, task) ->
if(partner.sharingState.orchestratorRecordId == null) partner.sharingState.orchestratorRecordId = UUID.fromString(task.recordId)
sharingStateService.setPending(partner.sharingState, task.taskId)
}

logger.info { "Created ${createdTasks.size} new golden record tasks from ready business partners" }
}

private fun createGoldenRecordTasks(mode: TaskMode, orchestratorBusinessPartnersDto: List<BusinessPartner>): List<TaskClientStateDto> {
private fun createGoldenRecordTasks(mode: TaskMode, orchestratorBusinessPartnersDto: List<TaskCreateRequestEntry>): List<TaskClientStateDto> {
if (orchestratorBusinessPartnersDto.isEmpty())
return emptyList()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE sharing_states
ADD COLUMN orchestrator_record_id UUID unique;
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ internal class BusinessPartnerIT @Autowired constructor(
return SharingStateDb(
externalId = "testExternalId",
sharingErrorCode = null,
orchestratorRecordId = null,
sharingStateType = SharingStateType.Initial
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class MockAndAssertUtils @Autowired constructor(
TaskClientStateDto(
taskId = "0",
businessPartnerResult = BusinessPartnerGenericCommonValues.businessPartner1,
recordId = "e3a05ebc-ff59-4d09-bd58-da31d6245701",
processingState = TaskProcessingStateDto(
resultState = ResultState.Pending,
step = TaskStep.CleanAndSync,
Expand All @@ -77,6 +78,7 @@ class MockAndAssertUtils @Autowired constructor(
TaskClientStateDto(
taskId = "1",
businessPartnerResult = BusinessPartnerGenericCommonValues.businessPartner1,
recordId = "f05574ff-4ddd-4360-821a-923203711f85",
processingState = TaskProcessingStateDto(
resultState = ResultState.Pending,
step = TaskStep.CleanAndSync,
Expand All @@ -90,6 +92,7 @@ class MockAndAssertUtils @Autowired constructor(
TaskClientStateDto(
taskId = "2",
businessPartnerResult = BusinessPartnerGenericCommonValues.businessPartner1,
recordId = "8c03850d-d772-4a2e-9845-65211231b38c",
processingState = TaskProcessingStateDto(
resultState = ResultState.Pending,
step = TaskStep.CleanAndSync,
Expand All @@ -103,6 +106,7 @@ class MockAndAssertUtils @Autowired constructor(
TaskClientStateDto(
taskId = "3",
businessPartnerResult = BusinessPartnerGenericCommonValues.businessPartner1,
recordId = "6bebb1aa-935d-467a-afd4-7e8623420a18",
processingState = TaskProcessingStateDto(
resultState = ResultState.Pending,
step = TaskStep.CleanAndSync,
Expand Down Expand Up @@ -132,6 +136,7 @@ class MockAndAssertUtils @Autowired constructor(
TaskClientStateDto(
taskId = "0",
businessPartnerResult = BusinessPartnerGenericCommonValues.businessPartner1,
recordId = "e3a05ebc-ff59-4d09-bd58-da31d6245701",
processingState = TaskProcessingStateDto(
resultState = ResultState.Success,
step = TaskStep.CleanAndSync,
Expand All @@ -145,6 +150,7 @@ class MockAndAssertUtils @Autowired constructor(
TaskClientStateDto(
taskId = "1",
businessPartnerResult = BusinessPartnerGenericCommonValues.businessPartner1,
recordId = "f05574ff-4ddd-4360-821a-923203711f85",
processingState = TaskProcessingStateDto(
resultState = ResultState.Error,
step = TaskStep.CleanAndSync,
Expand Down Expand Up @@ -172,7 +178,10 @@ class MockAndAssertUtils @Autowired constructor(
val taskStateResponse = TaskStateResponse(
listOf(
TaskClientStateDto(
taskId = "0", businessPartnerResult = BusinessPartnerGenericCommonValues.businessPartner1, processingState = TaskProcessingStateDto(
taskId = "0",
businessPartnerResult = BusinessPartnerGenericCommonValues.businessPartner1,
recordId = "e3a05ebc-ff59-4d09-bd58-da31d6245701",
processingState = TaskProcessingStateDto(
resultState = ResultState.Success,
step = TaskStep.CleanAndSync,
stepState = StepState.Queued,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ data class TaskClientStateDto(
@get:Schema(required = true)
val taskId: String,

@get:Schema(required = true, description = "The identifier of the gate record for which this task has been created")
val recordId: String,

val businessPartnerResult: BusinessPartner,

@get:Schema(required = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ data class TaskCreateRequest(
@get:Schema(required = true, description = "The mode affecting which processing steps the business partner goes through")
val mode: TaskMode,

@get:ArraySchema(arraySchema = Schema(description = "The list of business partner data to be processed"))
val businessPartners: List<BusinessPartner>
@get:ArraySchema(arraySchema = Schema(description = "The list of tasks to create"))
val requests: List<TaskCreateRequestEntry>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (c) 2021,2024 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.orchestrator.api.model

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

data class TaskCreateRequestEntry(
@get:Schema(description = "The unique identifier for this record which was previously issued by the Orchestrator")
val recordId: String?,
@get:Schema(description = "The business partner data to be processed")
val businessPartner: BusinessPartner
)
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ data class TaskStepReservationEntryDto(
@get:Schema(description = "The identifier of the reserved task")
val taskId: String,

@get:Schema(description = "The identifier of the gate record for which this task has been created")
val recordId: String,

@get:Schema(description = "The business partner data to process")
val businessPartner: BusinessPartner
) : RequestWithKey {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ class GoldenRecordTaskController(

@PreAuthorize("hasAuthority(${PermissionConfigProperties.CREATE_TASK})")
override fun createTasks(createRequest: TaskCreateRequest): TaskCreateResponse {
if (createRequest.businessPartners.size > apiConfigProperties.upsertLimit)
throw BpdmUpsertLimitException(createRequest.businessPartners.size, apiConfigProperties.upsertLimit)
if (createRequest.requests.size > apiConfigProperties.upsertLimit)
throw BpdmUpsertLimitException(createRequest.requests.size, apiConfigProperties.upsertLimit)

return goldenRecordTaskService.createTasks(createRequest)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ class DbTimestampConverter: AttributeConverter<DbTimestamp, Timestamp> {
}

override fun convertToEntityAttribute(p0: Timestamp?): DbTimestamp? {
return p0?.let { DbTimestamp(p0.toInstant()) }
return p0?.let { DbTimestamp(p0.toInstant()) }
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*******************************************************************************
* Copyright (c) 2021,2024 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.orchestrator.entity

import jakarta.persistence.*
import org.hibernate.annotations.CreationTimestamp
import org.hibernate.annotations.UpdateTimestamp
import java.time.Instant
import java.util.*

@Entity
@Table(
name = "gate_records",
indexes = [
Index(name = "index_gate_records_private_uuid", columnList = "private_uuid")
]
)
class GateRecordDb (
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "bpdm_sequence")
@SequenceGenerator(name = "bpdm_sequence", sequenceName = "bpdm_sequence", allocationSize = 1)
@Column(name = "id", nullable = false, updatable = false, insertable = false)
val id: Long = 0,

@Column(updatable = false, nullable = false, name = "CREATED_AT")
@CreationTimestamp
var createdAt: Instant = Instant.now(),

@Column(nullable = false, name = "UPDATED_AT")
@UpdateTimestamp
var updatedAt: Instant = Instant.now(),

@Column(name = "public_id", columnDefinition = "UUID", nullable = false, unique = true)
var publicId: UUID,

@Column(name = "private_id", columnDefinition = "UUID", nullable = false, unique = true)
var privateId: UUID
)
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ class GoldenRecordTaskDb(
@Column(nullable = false, name = "UPDATED_AT")
@Convert(converter = DbTimestampConverter::class)
var updatedAt: DbTimestamp = createdAt,
@ManyToOne
@JoinColumn(name = "gate_record_id", nullable = false, foreignKey = ForeignKey(name = "fk_tasks_gate_records"))
var gateRecord: GateRecordDb,

@Embedded
val processingState: ProcessingState,
@Embedded
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (c) 2021,2024 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.orchestrator.exception

import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.ResponseStatus
import java.util.*

@ResponseStatus(HttpStatus.BAD_REQUEST)
class BpdmRecordNotFoundException (
recordIds: List<UUID>
): RuntimeException("The following gate records are not registered: ${recordIds.joinToString()}")
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*******************************************************************************
* Copyright (c) 2021,2024 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.orchestrator.repository

import org.eclipse.tractusx.bpdm.orchestrator.entity.GateRecordDb
import org.springframework.data.repository.CrudRepository
import java.util.*

interface GateRecordRepository: CrudRepository<GateRecordDb, Long> {

fun findByPrivateIdIn(privateUuids: Set<UUID>): Set<GateRecordDb>
}
Loading

0 comments on commit a7466bb

Please sign in to comment.