Skip to content

Commit

Permalink
Merge pull request #980 from SujitMBRDI/feat/update_partner_upload_en…
Browse files Browse the repository at this point in the history
…dpoint

feat(bpdm-gate): updated upload partner process to only process site and address partners
  • Loading branch information
SujitMBRDI authored Jul 8, 2024
2 parents 5ea8c20 + da24adf commit 201db14
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class KeyCloakInitializer: ApplicationContextInitializer<ConfigurableApplication
.withRealmImportFile("keycloak/CX-Central.json")

const val REALM = "CX-Central"
const val TENANT_BPNL = "BPNL000000000010"
}

override fun initialize(applicationContext: ConfigurableApplicationContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2626,7 +2626,7 @@
"id.token.claim" : "false",
"lightweight.claim" : "false",
"access.token.claim" : "true",
"claim.name" : "BPN",
"claim.name" : "bpn",
"jsonType.label" : "String"
}
} ]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,6 @@ object PartnerUploadFileHeader {
const val STATES_TYPE_2 = "states2.type"
const val ROLES = "roles"
const val IS_OWN_COMPANY_DATA = "isOwnCompanyData"
const val LEGAL_ENTITY_BPN = "legalEntity.legalEntityBpn"
const val LEGAL_ENTITY_NAME = "legalEntity.legalName"
const val LEGAL_ENTITY_SHORT_NAME = "legalEntity.shortName"
const val LEGAL_ENTITY_LEGAL_FORM = "legalEntity.legalForm"
const val LEGAL_ENTITY_STATES_VALID_FROM = "legalEntity.states.validFrom"
const val LEGAL_ENTITY_STATES_VALID_TO = "legalEntity.states.validTo"
const val LEGAL_ENTITY_STATES_TYPE = "legalEntity.states.type"
const val SITE_BPN = "site.siteBpn"
const val SITE_NAME = "site.name"
const val SITE_STATES_VALID_FROM = "site.states.validFrom"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,27 +91,6 @@ data class PartnerUploadFileRow(
@CsvBindByName(column = PartnerUploadFileHeader.IS_OWN_COMPANY_DATA)
val isOwnCompanyData: String? = null,

@CsvBindByName(column = PartnerUploadFileHeader.LEGAL_ENTITY_BPN)
val legalEntityBpn: String? = null,

@CsvBindByName(column = PartnerUploadFileHeader.LEGAL_ENTITY_NAME)
val legalEntityName: String? = null,

@CsvBindByName(column = PartnerUploadFileHeader.LEGAL_ENTITY_SHORT_NAME)
val legalEntityShortName: String? = null,

@CsvBindByName(column = PartnerUploadFileHeader.LEGAL_ENTITY_LEGAL_FORM)
val legalEntityLegalForm: String? = null,

@CsvBindByName(column = PartnerUploadFileHeader.LEGAL_ENTITY_STATES_VALID_FROM)
val legalEntityStatesValidFrom: String? = null,

@CsvBindByName(column = PartnerUploadFileHeader.LEGAL_ENTITY_STATES_VALID_TO)
val legalEntityStatesValidTo: String? = null,

@CsvBindByName(column = PartnerUploadFileHeader.LEGAL_ENTITY_STATES_TYPE)
val legalEntityStatesType: String? = null,

@CsvBindByName(column = PartnerUploadFileHeader.SITE_BPN)
val siteBpn: String? = null,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ class PartnerUploadService(

private val logger = KotlinLogging.logger { }

fun processFile(file: MultipartFile, ownerBpnl: String?): ResponseEntity<Collection<BusinessPartnerInputDto>> {
fun processFile(file: MultipartFile, tenantBpnl: String?): ResponseEntity<Collection<BusinessPartnerInputDto>> {
validateTenantBpnl(tenantBpnl)
val csvData: List<PartnerUploadFileRow> = PartnerFileUtil.parseCsv(file)
val businessPartnerDtos = PartnerFileUtil.validateAndMapToBusinessPartnerInputRequests(csvData)
val result = businessPartnerService.upsertBusinessPartnersInput(businessPartnerDtos, ownerBpnl)
val businessPartnerDtos = PartnerFileUtil.validateAndMapToBusinessPartnerInputRequests(csvData, tenantBpnl)
val result = businessPartnerService.upsertBusinessPartnersInput(businessPartnerDtos, tenantBpnl)
return ResponseEntity.ok(result)
}

Expand All @@ -66,4 +67,10 @@ class PartnerUploadService(
return ByteArrayResource(outputStream.toByteArray())
}

private fun validateTenantBpnl(tenantBpnl: String?) {
if (tenantBpnl.isNullOrEmpty() || !tenantBpnl.startsWith("BPNL")) {
throw IllegalArgumentException("tenantBpnl must not be null or empty and must start with 'BPNL'")
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ object PartnerFileUtil {
* @return A list of BusinessPartnerInputRequest objects derived from the valid CSV rows.
* @throws BpdmInvalidPartnerUploadException if any validation errors are encountered during processing.
*/
fun validateAndMapToBusinessPartnerInputRequests(csvData: List<PartnerUploadFileRow>): List<BusinessPartnerInputRequest> {
fun validateAndMapToBusinessPartnerInputRequests(csvData: List<PartnerUploadFileRow>, tenantBpnl: String?): List<BusinessPartnerInputRequest> {
val formatter = DateTimeFormatter.ISO_DATE_TIME
val validator: Validator = Validation.buildDefaultValidatorFactory().validator
val errors = mutableListOf<String>()
Expand All @@ -89,7 +89,8 @@ object PartnerFileUtil {
),
roles = row.roles.toEnumList(BusinessPartnerRole::valueOf, errors, index + 1, PartnerUploadFileHeader.ROLES),
isOwnCompanyData = row.isOwnCompanyData?.toBoolean() ?: false,
legalEntity = row.toLegalEntityRepresentationInputDto(formatter, errors, index),
// Legal entity's business partner number is nothing but tenant's partner number who is performing business partner upload action
legalEntity = LegalEntityRepresentationInputDto(legalEntityBpn = tenantBpnl?.takeIf { it.isNotEmpty() }),
site = row.toSiteRepresentationInputDto(formatter, errors, index),
address = row.toAddressRepresentationInputDto(formatter, errors, index)
)
Expand Down Expand Up @@ -180,27 +181,6 @@ object PartnerFileUtil {
)
}

private fun PartnerUploadFileRow.toLegalEntityRepresentationInputDto(
formatter: DateTimeFormatter,
errors: MutableList<String>,
rowIndex: Int
): LegalEntityRepresentationInputDto {
return LegalEntityRepresentationInputDto(
legalEntityBpn = legalEntityBpn?.takeIf { it.isNotEmpty() },
legalName = legalEntityName?.takeIf { it.isNotEmpty() },
shortName = legalEntityShortName?.takeIf { it.isNotEmpty() },
legalForm = legalEntityLegalForm?.takeIf { it.isNotEmpty() },
states = listOfNotNull(
if (!legalEntityStatesValidFrom.isNullOrEmpty() && !legalEntityStatesValidTo.isNullOrEmpty() && !legalEntityStatesType.isNullOrEmpty())
BusinessPartnerStateDto(
validFrom = parseDate(legalEntityStatesValidFrom, formatter, errors, rowIndex + 1, PartnerUploadFileHeader.LEGAL_ENTITY_STATES_VALID_FROM),
validTo = parseDate(legalEntityStatesValidTo, formatter, errors, rowIndex + 1, PartnerUploadFileHeader.LEGAL_ENTITY_STATES_VALID_TO),
type = parseEnum(legalEntityStatesType, BusinessStateType::valueOf, errors, rowIndex + 1, PartnerUploadFileHeader.LEGAL_ENTITY_STATES_TYPE)
) else null
)
)
}

private fun PartnerUploadFileRow.toSiteRepresentationInputDto(
formatter: DateTimeFormatter,
errors: MutableList<String>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,74 +19,49 @@

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

import com.github.tomakehurst.wiremock.core.WireMockConfiguration
import com.github.tomakehurst.wiremock.junit5.WireMockExtension
import org.eclipse.tractusx.bpdm.gate.api.client.GateClient
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.SharingStateDto
import org.eclipse.tractusx.bpdm.gate.service.TaskCreationService
import org.eclipse.tractusx.bpdm.gate.api.model.response.LegalEntityRepresentationInputDto
import org.eclipse.tractusx.bpdm.gate.auth.AuthAdminIT
import org.eclipse.tractusx.bpdm.gate.util.MockAndAssertUtils
import org.eclipse.tractusx.bpdm.test.containers.KeyCloakInitializer
import org.eclipse.tractusx.bpdm.test.containers.PostgreSQLContextInitializer
import org.eclipse.tractusx.bpdm.test.testdata.gate.BusinessPartnerNonVerboseValues
import org.eclipse.tractusx.bpdm.test.testdata.gate.BusinessPartnerVerboseValues
import org.eclipse.tractusx.bpdm.test.util.AssertHelpers
import org.eclipse.tractusx.bpdm.test.util.DbTestHelpers
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.RegisterExtension
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.HttpStatus
import org.springframework.mock.web.MockMultipartFile
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 org.springframework.web.reactive.function.client.WebClientResponseException
import java.nio.file.Files
import java.nio.file.Paths

@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
)
@ActiveProfiles("test")
@ContextConfiguration(
initializers = [
PostgreSQLContextInitializer::class,
KeyCloakInitializer::class,
AuthAdminIT.SelfClientAsAdminInitializer::class
]
)
@ActiveProfiles("test-no-auth")
@ContextConfiguration(initializers = [PostgreSQLContextInitializer::class])
class PartnerUploadControllerIT @Autowired constructor(
val testHelpers: DbTestHelpers,
val assertHelpers: AssertHelpers,
val gateClient: GateClient,
val taskCreationService: TaskCreationService,
val mockAndAssertUtils: MockAndAssertUtils
){

companion object {

@JvmField
@RegisterExtension
val orchestratorWireMockServer: WireMockExtension = WireMockExtension.newInstance().options(WireMockConfiguration.wireMockConfig().dynamicPort()).build()

@JvmField
@RegisterExtension
val poolWireMockServer: WireMockExtension = WireMockExtension.newInstance().options(WireMockConfiguration.wireMockConfig().dynamicPort()).build()


@JvmStatic
@DynamicPropertySource
fun properties(registry: DynamicPropertyRegistry) {
registry.add("bpdm.client.pool.base-url") { poolWireMockServer.baseUrl() }
registry.add("bpdm.client.orchestrator.base-url") { orchestratorWireMockServer.baseUrl() }
}
}
) {

@BeforeEach
fun beforeEach() {
testHelpers.truncateDbTables()
orchestratorWireMockServer.resetAll()
poolWireMockServer.resetAll()
this.mockAndAssertUtils.mockOrchestratorApi(orchestratorWireMockServer)
}

@Test
Expand All @@ -104,55 +79,35 @@ class PartnerUploadControllerIT @Autowired constructor(
testFileUpload("src/test/resources/testData/invalid_partner_data.csv", HttpStatus.BAD_REQUEST)
}

@Test
fun testUploadPartnerDataAndCheckSharingState() {
val bytes = Files.readAllBytes(Paths.get("src/test/resources/testData/valid_partner_data.csv"))
val uploadedFile = MockMultipartFile("valid_partner_data.csv", "valid_partner_data.csv", "text/csv", bytes)

uploadBusinessPartnerRecordAndShare(uploadedFile)

val externalId1 = BusinessPartnerVerboseValues.externalId1
val externalId2 = BusinessPartnerVerboseValues.externalId2

val externalIds = listOf(externalId1, externalId2)

val sharingStatesRequests = listOf(
SharingStateDto(
externalId = externalId1,
sharingStateType = SharingStateType.Pending,
sharingErrorCode = null,
sharingErrorMessage = null,
sharingProcessStarted = null,
taskId = "0"
),
SharingStateDto(
externalId = externalId2,
sharingStateType = SharingStateType.Pending,
sharingErrorCode = null,
sharingErrorMessage = null,
sharingProcessStarted = null,
taskId = "1"
)
)

val sharingStateResponses = this.mockAndAssertUtils.readSharingStates(externalIds)
assertHelpers.assertRecursively(sharingStateResponses).isEqualTo(sharingStatesRequests)
}

@Test
fun testUploadCsvAndValidateBusinessPartnerData() {
// Read the bytes of the CSV file
val bytes = Files.readAllBytes(Paths.get("src/test/resources/testData/valid_partner_data.csv"))
val uploadedFile = MockMultipartFile("valid_partner_data.csv", "valid_partner_data.csv", "text/csv", bytes)

// Only Site and Address expected to be updated from upload partner process.
val expectedSiteAndAddressPartner = BusinessPartnerVerboseValues.bpInputRequestFull.copy(
legalEntity = LegalEntityRepresentationInputDto(
legalEntityBpn = KeyCloakInitializer.TENANT_BPNL,
legalName = null,
shortName = null,
legalForm = null,
states = emptyList()
)
)
// Upload the CSV file
val uploadResponse = gateClient.partnerUpload.uploadPartnerCsvFile(uploadedFile).body!!
val expectedResponse = listOf(
BusinessPartnerVerboseValues.bpInputRequestFull,
BusinessPartnerNonVerboseValues.bpInputRequestFull.fastCopy(externalId = BusinessPartnerVerboseValues.externalId2, shortName = "2")
expectedSiteAndAddressPartner,
expectedSiteAndAddressPartner.fastCopy(externalId = BusinessPartnerVerboseValues.externalId2, siteName = "Site Name 2")
)

val searchResponsePage = gateClient.businessParters.getBusinessPartnersInput(listOf(BusinessPartnerVerboseValues.externalId1, BusinessPartnerVerboseValues.externalId2)).content
val searchResponsePage = gateClient.businessParters.getBusinessPartnersInput(
listOf(
BusinessPartnerVerboseValues.externalId1,
BusinessPartnerVerboseValues.externalId2
)
).content
this.mockAndAssertUtils.assertUpsertResponsesMatchRequests(uploadResponse, expectedResponse)
this.mockAndAssertUtils.assertUpsertResponsesMatchRequests(searchResponsePage, expectedResponse)
}
Expand All @@ -175,13 +130,28 @@ class PartnerUploadControllerIT @Autowired constructor(
val combinedFile = MockMultipartFile("combined_partner_data.csv", "combined_partner_data.csv", "text/csv", combinedCsv.toByteArray())
val uploadResponse = gateClient.partnerUpload.uploadPartnerCsvFile(combinedFile).body!!

val expectedSiteAndAddressPartner = BusinessPartnerVerboseValues.bpInputRequestFull.copy(
legalEntity = LegalEntityRepresentationInputDto(
legalEntityBpn = KeyCloakInitializer.TENANT_BPNL,
legalName = null,
shortName = null,
legalForm = null,
states = emptyList()
)
)

// Perform assertions
val expectedResponse = listOf(
BusinessPartnerVerboseValues.bpInputRequestFull,
BusinessPartnerNonVerboseValues.bpInputRequestFull.fastCopy(externalId = BusinessPartnerVerboseValues.externalId2, shortName = "2")
expectedSiteAndAddressPartner,
expectedSiteAndAddressPartner.fastCopy(externalId = BusinessPartnerVerboseValues.externalId2, siteName = "Site Name 2")
)

val searchResponsePage = gateClient.businessParters.getBusinessPartnersInput(listOf(BusinessPartnerVerboseValues.externalId1, BusinessPartnerVerboseValues.externalId2)).content
val searchResponsePage = gateClient.businessParters.getBusinessPartnersInput(
listOf(
BusinessPartnerVerboseValues.externalId1,
BusinessPartnerVerboseValues.externalId2
)
).content
this.mockAndAssertUtils.assertUpsertResponsesMatchRequests(uploadResponse, expectedResponse)
this.mockAndAssertUtils.assertUpsertResponsesMatchRequests(searchResponsePage, expectedResponse)
}
Expand Down Expand Up @@ -210,12 +180,7 @@ class PartnerUploadControllerIT @Autowired constructor(
}
}

private fun uploadBusinessPartnerRecordAndShare(file: MockMultipartFile) {
gateClient.partnerUpload.uploadPartnerCsvFile(file)
taskCreationService.createTasksForReadyBusinessPartners()
}

private fun BusinessPartnerInputRequest.fastCopy(externalId: String, shortName: String) =
copy(externalId = externalId, legalEntity = legalEntity.copy(shortName = shortName))
private fun BusinessPartnerInputRequest.fastCopy(externalId: String, siteName: String) =
copy(externalId = externalId, site = site.copy(name = siteName))

}
2 changes: 1 addition & 1 deletion bpdm-gate/src/test/resources/application-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ bpdm:
fromSharingMember:
cron: '-'
fromPool:
cron: '_'
cron: '-'
check:
cron: '-'
Loading

0 comments on commit 201db14

Please sign in to comment.