diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt index 9f4f49157..ca645c173 100644 --- a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateController.kt @@ -24,79 +24,28 @@ 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.GateSharingStateApi import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateDto -import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateType import org.eclipse.tractusx.bpdm.gate.api.model.response.LsaType -import org.eclipse.tractusx.bpdm.gate.service.AddressService -import org.eclipse.tractusx.bpdm.gate.service.LegalEntityService -import org.eclipse.tractusx.bpdm.gate.service.SiteService +import org.eclipse.tractusx.bpdm.gate.service.SharingStateService import org.springframework.web.bind.annotation.RestController @RestController class SharingStateController( - val legalEntityService: LegalEntityService, - val siteService: SiteService, - val addressService: AddressService + val sharingStateService: SharingStateService ) : GateSharingStateApi { private val logger = KotlinLogging.logger { } override fun getSharingStates(paginationRequest: PaginationRequest, lsaType: LsaType?, externalIds: Collection?): PageResponse { - // TODO Replace mock implementation using persistence: - // For now only the BPN is collected from CDQ input. If there is none, no sharing status is returned. - // For now lsaType is required! + lsaType ?: throw IllegalArgumentException("lsaType is required") externalIds ?: throw IllegalArgumentException("externalIds is required") - val sharingStates = when (lsaType) { - LsaType.LegalEntity -> - legalEntityService.getLegalEntities(externalIds = externalIds, limit = paginationRequest.size, startAfter = null).content - .filter { it.bpn != null } - .map { - SharingStateDto( - lsaType = lsaType, - externalId = it.externalId, - bpn = it.bpn, - sharingStateType = SharingStateType.Success - ) - } - - LsaType.Site -> - siteService.getSites(externalIds = externalIds, limit = paginationRequest.size, startAfter = null).content - .filter { it.bpn != null } - .map { - SharingStateDto( - lsaType = lsaType, - externalId = it.externalId, - bpn = it.bpn, - sharingStateType = SharingStateType.Success - ) - } - - LsaType.Address -> - addressService.getAddresses(externalIds = externalIds, limit = paginationRequest.size, startAfter = null).content - .filter { it.bpn != null } - .map { - SharingStateDto( - lsaType = lsaType, - externalId = it.externalId, - bpn = it.bpn, - sharingStateType = SharingStateType.Success - ) - } - } - - // TODO Not yet implemented - return PageResponse( - totalElements = sharingStates.size.toLong(), - totalPages = if (sharingStates.size > 0) 1 else 0, - page = 0, - contentSize = sharingStates.size, - content = sharingStates - ) + return sharingStateService.findSharingStates(paginationRequest, lsaType, externalIds) } override fun upsertSharingState(request: SharingStateDto) { // TODO Not yet implemented logger.info { "upsertSharingState() called with $request" } + sharingStateService.upsertSharingState(request) } } diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/entity/SharingState.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/entity/SharingState.kt new file mode 100644 index 000000000..1abd98360 --- /dev/null +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/entity/SharingState.kt @@ -0,0 +1,61 @@ +/******************************************************************************* + * 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.entity + +import jakarta.persistence.* +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 org.eclipse.tractusx.bpdm.gate.api.model.response.LsaType +import java.time.LocalDateTime + + +@Entity +@Table(name = "sharing_states") +class SharingState( + + @Column(name = "external_id", nullable = false) + var externalId: String, + + @Enumerated(EnumType.STRING) + @Column(name = "lsa_type", nullable = false) + var lsaType: LsaType, + + @Enumerated(EnumType.STRING) + @Column(name = "sharing_state_type", nullable = false) + var sharingStateType: SharingStateType, + + @Enumerated(EnumType.STRING) + @Column(name = "sharing_error_code", nullable = true) + var sharingErrorCode: BusinessPartnerSharingError?, + + @Column(name = "sharing_error_message", nullable = true) + var sharingErrorMessage: String? = null, + + @Column(name = "bpn", nullable = true) + var bpn: String? = null, + + @Column(name = "sharing_process_started", nullable = true) + var sharingProcessStarted: LocalDateTime? = null + +) : BaseEntity() + + + diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStaterRepository.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStaterRepository.kt new file mode 100644 index 000000000..04e27ac5e --- /dev/null +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/repository/SharingStaterRepository.kt @@ -0,0 +1,35 @@ +/******************************************************************************* + * 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.repository + +import org.eclipse.tractusx.bpdm.gate.api.model.response.LsaType +import org.eclipse.tractusx.bpdm.gate.entity.SharingState +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 SharingStaterRepository : PagingAndSortingRepository, CrudRepository { + + fun findByExternalIdAndLsaType(externalId: String, businessPartnerType: LsaType): SharingState? + + fun findByExternalIdInAndLsaType(externalIds: Collection, businessPartnerType: LsaType, pageable: Pageable): Page + +} \ No newline at end of file diff --git a/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/SharingStateService.kt b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/SharingStateService.kt new file mode 100644 index 000000000..8fccf99e8 --- /dev/null +++ b/bpdm-gate/src/main/kotlin/org/eclipse/tractusx/bpdm/gate/service/SharingStateService.kt @@ -0,0 +1,88 @@ +/******************************************************************************* + * 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.service + +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.SharingStateDto +import org.eclipse.tractusx.bpdm.gate.api.model.response.LsaType +import org.eclipse.tractusx.bpdm.gate.entity.SharingState +import org.eclipse.tractusx.bpdm.gate.repository.SharingStaterRepository +import org.springframework.data.domain.PageRequest +import org.springframework.stereotype.Service + +@Service +class SharingStateService(private val stateRepository: SharingStaterRepository) { + + fun upsertSharingState(request: SharingStateDto) { + + val sharingState = this.stateRepository.findByExternalIdAndLsaType(request.externalId, request.lsaType) + if (sharingState == null) { + insertSharingState(request) + } else { + updateSharingState(sharingState, request) + } + } + + private fun insertSharingState(dto: SharingStateDto) { + + this.stateRepository.save( + SharingState( + externalId = dto.externalId, + lsaType = dto.lsaType, + sharingStateType = dto.sharingStateType, + sharingErrorCode = dto.sharingErrorCode, + sharingErrorMessage = dto.sharingErrorMessage, + bpn = dto.bpn, + sharingProcessStarted = dto.sharingProcessStarted + ) + ) + } + + private fun updateSharingState(entity: SharingState, dto: SharingStateDto) { + + entity.sharingStateType = dto.sharingStateType + entity.sharingErrorCode = dto.sharingErrorCode + entity.sharingErrorMessage = dto.sharingErrorMessage + entity.bpn = dto.bpn + entity.sharingProcessStarted = dto.sharingProcessStarted + + this.stateRepository.save(entity) + } + + fun findSharingStates(paginationRequest: PaginationRequest, lsaType: LsaType, externalIds: Collection): PageResponse { + + val pageable = PageRequest.of(paginationRequest.page, paginationRequest.size) + val page = this.stateRepository.findByExternalIdInAndLsaType(externalIds, lsaType, pageable) + + return page.toDto(page.content.map { + SharingStateDto( + externalId = it.externalId, + lsaType = it.lsaType, + sharingStateType = it.sharingStateType, + sharingErrorCode = it.sharingErrorCode, + sharingErrorMessage = it.sharingErrorMessage, + bpn = it.bpn, + sharingProcessStarted = it.sharingProcessStarted + ) + }) + + } +} \ No newline at end of file diff --git a/bpdm-gate/src/main/resources/db/migration/V0_0_2_0__create_sharingstate_table.sql b/bpdm-gate/src/main/resources/db/migration/V0_0_2_0__create_sharingstate_table.sql new file mode 100644 index 000000000..97d53665c --- /dev/null +++ b/bpdm-gate/src/main/resources/db/migration/V0_0_2_0__create_sharingstate_table.sql @@ -0,0 +1,28 @@ + +CREATE TABLE sharing_states +( + id BIGINT NOT NULL, + uuid UUID NOT NULL, + created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + external_id VARCHAR(255) NOT NULL, + lsa_type VARCHAR(255) NOT NULL, + sharing_state_type VARCHAR(255) NOT NULL, + sharing_error_code VARCHAR(255), + sharing_error_message VARCHAR(255), + bpn VARCHAR(255), + sharing_process_started timestamp with time zone, + CONSTRAINT pk_sharing_states PRIMARY KEY (id) +); + +ALTER TABLE sharing_states + ADD CONSTRAINT uc_sharing_states_uuid UNIQUE (uuid); +ALTER TABLE sharing_states + ADD CONSTRAINT uc_sharing_states_externalId_lsa_type UNIQUE (external_id, lsa_type); + +alter table sharing_states + alter column created_at type timestamp with time zone using created_at at time zone 'UTC'; +alter table sharing_states + alter column updated_at type timestamp with time zone using updated_at at time zone 'UTC'; + alter table sharing_states + alter column sharing_process_started type timestamp with time zone using sharing_process_started at time zone 'UTC'; diff --git a/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/ChangeLogControllerIT.kt b/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/ChangeLogControllerIT.kt index b9d073725..9b7888a4b 100644 --- a/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/ChangeLogControllerIT.kt +++ b/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/ChangeLogControllerIT.kt @@ -42,8 +42,6 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.github.tomakehurst.wiremock.client.WireMock.* import com.github.tomakehurst.wiremock.core.WireMockConfiguration import com.github.tomakehurst.wiremock.junit5.WireMockExtension -import jakarta.persistence.EntityManager -import jakarta.persistence.EntityManagerFactory import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.RecursiveComparisonAssert import org.eclipse.tractusx.bpdm.common.dto.request.PaginationRequest @@ -54,9 +52,7 @@ import org.eclipse.tractusx.bpdm.gate.api.model.response.ChangelogResponse import org.eclipse.tractusx.bpdm.gate.api.model.response.ErrorInfo import org.eclipse.tractusx.bpdm.gate.config.SaasConfigProperties import org.eclipse.tractusx.bpdm.gate.entity.ChangelogEntry - import org.eclipse.tractusx.bpdm.gate.util.* - import org.eclipse.tractusx.bpdm.gate.util.CommonValues.lsaTypeParam import org.eclipse.tractusx.bpdm.gate.util.CommonValues.lsaTypeParamNotFound import org.junit.jupiter.api.BeforeEach @@ -68,7 +64,6 @@ 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 java.time.Instant @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @@ -78,7 +73,7 @@ internal class ChangeLogControllerIT @Autowired constructor( val gateClient: GateClient, private val objectMapper: ObjectMapper, val saasConfigProperties: SaasConfigProperties, - entityManagerFactory: EntityManagerFactory, + private val testHelpers: DbTestHelpers, ) { companion object { @RegisterExtension @@ -96,13 +91,9 @@ internal class ChangeLogControllerIT @Autowired constructor( val instant = Instant.now() - val BPDM_DB_SCHEMA_NAME: String = "bpdmgate" - - val em: EntityManager = entityManagerFactory.createEntityManager() - @BeforeEach fun beforeEach() { - truncateDbTables() + testHelpers.truncateDbTables() wireMockServer.resetAll() mockSaas() createChangeLogs() @@ -124,7 +115,6 @@ internal class ChangeLogControllerIT @Autowired constructor( } - /** * Given externalId does not exist in database * When getting changeLog by external id @@ -145,13 +135,15 @@ internal class ChangeLogControllerIT @Autowired constructor( .isEqualTo(emptyList()) assertRecursively(searchResult.errors) - .isEqualTo(listOf( - ErrorInfo( - ChangeLogOutputError.ExternalIdNotFound, - "NONEXIST not found", - "NONEXIST") - )) - + .isEqualTo( + listOf( + ErrorInfo( + ChangeLogOutputError.ExternalIdNotFound, + "NONEXIST not found", + "NONEXIST" + ) + ) + ) } @@ -241,7 +233,6 @@ internal class ChangeLogControllerIT @Autowired constructor( ) - // mock "get parent legal entities" wireMockServer.stubFor( get(urlPathMatching(EndpointValues.SAAS_MOCK_BUSINESS_PARTNER_PATH)) @@ -353,26 +344,6 @@ internal class ChangeLogControllerIT @Autowired constructor( ) } - fun truncateDbTables() { - em.transaction.begin() - - em.createNativeQuery( - """ - DO $$ DECLARE table_names RECORD; - BEGIN - FOR table_names IN SELECT table_name - FROM information_schema.tables - WHERE table_schema='${BPDM_DB_SCHEMA_NAME}' - AND table_name NOT IN ('flyway_schema_history') - LOOP - EXECUTE format('TRUNCATE TABLE ${BPDM_DB_SCHEMA_NAME}.%I CONTINUE IDENTITY CASCADE;', table_names.table_name); - END LOOP; - END $$; - """.trimIndent() - ).executeUpdate() - - em.transaction.commit() - } fun assertRecursively(actual: T): RecursiveComparisonAssert<*> { return assertThat(actual) @@ -387,7 +358,7 @@ internal class ChangeLogControllerIT @Autowired constructor( * Retains the order: All response objects will be in the same order as their request counterparts * Assumption: Changelog entities have unique indexes among them each */ - fun createChangeLogs(){ + fun createChangeLogs() { val addresses = listOf( RequestValues.addressGateInputRequest1 ) diff --git a/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateControllerIT.kt b/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateControllerIT.kt new file mode 100644 index 000000000..63e3695cd --- /dev/null +++ b/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/controller/SharingStateControllerIT.kt @@ -0,0 +1,70 @@ +/******************************************************************************* + * 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.controller + +import org.assertj.core.api.Assertions +import org.eclipse.tractusx.bpdm.common.dto.request.PaginationRequest +import org.eclipse.tractusx.bpdm.gate.api.client.GateClient +import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateDto +import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateType +import org.eclipse.tractusx.bpdm.gate.api.model.response.LsaType +import org.eclipse.tractusx.bpdm.gate.util.DbTestHelpers +import org.eclipse.tractusx.bpdm.gate.util.PostgreSQLContextInitializer +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.context.ContextConfiguration + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ActiveProfiles("test") +@ContextConfiguration(initializers = [PostgreSQLContextInitializer::class]) +class SharingStateControllerIT @Autowired constructor( + + private val testHelpers: DbTestHelpers, + val gateClient: GateClient, +) { + + @BeforeEach + fun beforeEach() { + testHelpers.truncateDbTables() + } + + /** + * Given externalId exists in database + * When getting changeLog by external id + * Then changeLog mapped to the catena data model should be returned + */ + @Test + fun `get changeLog by external id`() { + + val state1 = SharingStateDto( + lsaType = LsaType.Address, externalId = "exId1", sharingStateType = SharingStateType.Success, + sharingErrorCode = null, sharingProcessStarted = null, sharingErrorMessage = null, bpn = null + ) + gateClient.sharingState().upsertSharingState(state1) + + val searchResult = gateClient.sharingState().getSharingStates(PaginationRequest(), LsaType.Address, listOf("exId1")) + Assertions.assertThat(searchResult.content.size).isEqualTo(1) + val searchResult2 = gateClient.sharingState().getSharingStates(PaginationRequest(), LsaType.Site, listOf("exId1")) + Assertions.assertThat(searchResult2.content.size).isEqualTo(0) + } +} \ No newline at end of file diff --git a/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/util/DbTestHelpers.kt b/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/util/DbTestHelpers.kt new file mode 100644 index 000000000..833fb5482 --- /dev/null +++ b/bpdm-gate/src/test/kotlin/org/eclipse/tractusx/bpdm/gate/util/DbTestHelpers.kt @@ -0,0 +1,54 @@ +/******************************************************************************* + * 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.util + +import jakarta.persistence.EntityManager +import jakarta.persistence.EntityManagerFactory +import org.springframework.stereotype.Component + +private const val BPDM_DB_SCHEMA_NAME: String = "bpdmgate" + +@Component +class DbTestHelpers(entityManagerFactory: EntityManagerFactory) { + + val em: EntityManager = entityManagerFactory.createEntityManager() + + fun truncateDbTables() { + em.transaction.begin() + + em.createNativeQuery( + """ + DO $$ DECLARE table_names RECORD; + BEGIN + FOR table_names IN SELECT table_name + FROM information_schema.tables + WHERE table_schema='$BPDM_DB_SCHEMA_NAME' + AND table_name NOT IN ('flyway_schema_history') + LOOP + EXECUTE format('TRUNCATE TABLE $BPDM_DB_SCHEMA_NAME.%I CONTINUE IDENTITY CASCADE;', table_names.table_name); + END LOOP; + END $$; + """.trimIndent() + ).executeUpdate() + + em.transaction.commit() + } + +} \ No newline at end of file