diff --git a/CHANGELOG.md b/CHANGELOG.md index df776b7964..671abfac4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] - +### Added +- DigitalTwinRegistryCreateShellService in irs-registry-client for creating shells in DTR directly ## [4.6.0] - 2024-02-20 @@ -28,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Moved Docker notice to separate file #425 + ## [4.5.0] - 2024-02-07 ### Added - Added helper script for building documentation locally. diff --git a/docs/src/api/irs-api.yaml b/docs/src/api/irs-api.yaml index f7c25939dc..eae3930a15 100644 --- a/docs/src/api/irs-api.yaml +++ b/docs/src/api/irs-api.yaml @@ -1077,7 +1077,7 @@ components: lexicalValue: piece quantityNumber: 1.0 shells: - - contractAgreementId: a787aa13-2bd7-488f-9e25-40682003901b + - contractAgreementId: f253718e-a270-4367-901b-9d50d9bd8462 payload: description: - language: en @@ -1131,6 +1131,7 @@ components: type: ModelReference submodels: - aspectType: supply_chain_impacted + contractAgreementId: f253718e-a270-4367-901b-9d50d9bd8462 identification: urn:uuid:fc784d2a-5506-4e61-8e34-21600f8cdeff payload: supplyChainImpacted: "YES" @@ -1199,7 +1200,7 @@ components: lexicalValue: piece quantityNumber: 1.0 shells: - - contractAgreementId: a787aa13-2bd7-488f-9e25-40682003901b + - contractAgreementId: f253718e-a270-4367-901b-9d50d9bd8462 payload: description: - language: en @@ -1253,6 +1254,7 @@ components: type: ModelReference submodels: - aspectType: urn:bamm:io.catenax.single_level_bom_as_built:1.0.0 + contractAgreementId: f253718e-a270-4367-901b-9d50d9bd8462 identification: urn:uuid:fc784d2a-5506-4e61-8e34-21600f8cdeff payload: catenaXId: urn:uuid:d9bec1c6-e47c-4d18-ba41-0a5fe8b7f447 @@ -1390,7 +1392,7 @@ components: lexicalValue: piece quantityNumber: 1.0 shells: - - contractAgreementId: a787aa13-2bd7-488f-9e25-40682003901b + - contractAgreementId: f253718e-a270-4367-901b-9d50d9bd8462 payload: description: - language: en @@ -1442,6 +1444,7 @@ components: value: urn:bamm:com.catenax.vehicle:0.1.1#PartDetails submodels: - aspectType: urn:bamm:io.catenax.single_level_bom_as_built:1.0.0 + contractAgreementId: f253718e-a270-4367-901b-9d50d9bd8462 identification: urn:uuid:fc784d2a-5506-4e61-8e34-21600f8cdeff payload: catenaXId: urn:uuid:d9bec1c6-e47c-4d18-ba41-0a5fe8b7f447 @@ -2326,6 +2329,11 @@ components: type: string subprotocolBodyEncoding: type: string + securityAttributes: + type: array + items: + $ref: '#/components/schemas/SecurityAttribute' + maxItems: 2147483647 Quantity: type: object additionalProperties: false @@ -2622,6 +2630,16 @@ components: value: type: string example: Submodel + SecurityAttribute: + type: object + additionalProperties: false + properties: + type: + type: string + key: + type: string + value: + type: string Shell: type: object additionalProperties: false diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/OpenApiExamples.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/OpenApiExamples.java index 7365543464..2e04d95ba3 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/OpenApiExamples.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/OpenApiExamples.java @@ -284,6 +284,7 @@ private Example createCompleteEssJobResult() { private Submodel createEssSubmodel() { return Submodel.builder() + .contractAgreementId(EXAMPLE_ID) .aspectType(SUPPLY_CHAIN_IMPACTED_ASPECT_TYPE) .identification(SUBMODEL_IDENTIFICATION) .payload(Map.of(SUPPLY_CHAIN_IMPACTED_KEY, SUPPLY_CHAIN_IMPACTER_RESULT)) @@ -343,6 +344,7 @@ private Example createCanceledJobResponse() { private Submodel createSubmodel() { return Submodel.builder() + .contractAgreementId(EXAMPLE_ID) .aspectType("urn:bamm:io.catenax.single_level_bom_as_built:1.0.0") .identification(SUBMODEL_IDENTIFICATION) .payload(createAssemblyPartRelationshipPayloadMap()) @@ -373,7 +375,7 @@ private Tombstone createTombstone() { } private Shell createShell() { - return new Shell("a787aa13-2bd7-488f-9e25-40682003901b", + return new Shell(EXAMPLE_ID, AssetAdministrationShellDescriptor.builder() .description(List.of(LangString.builder() .language("en") diff --git a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/assetadministrationshell/ProtocolInformation.java b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/assetadministrationshell/ProtocolInformation.java index e1c204c142..93e0650d87 100644 --- a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/assetadministrationshell/ProtocolInformation.java +++ b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/assetadministrationshell/ProtocolInformation.java @@ -66,5 +66,10 @@ public class ProtocolInformation { * subprotocolBodyEncoding */ private String subprotocolBodyEncoding; + /** + * securityAttributes + */ + @ArraySchema(arraySchema = @Schema(implementation = SecurityAttribute.class), maxItems = Integer.MAX_VALUE) + private List securityAttributes; } diff --git a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/assetadministrationshell/SecurityAttribute.java b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/assetadministrationshell/SecurityAttribute.java new file mode 100644 index 0000000000..4065b514a6 --- /dev/null +++ b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/assetadministrationshell/SecurityAttribute.java @@ -0,0 +1,51 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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.irs.component.assetadministrationshell; + +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; + +/** + * SecurityAttribute + */ +@Data +@Builder +@Jacksonized +public class SecurityAttribute { + + /** + * type + */ + private String type; + /** + * key + */ + private String key; + /** + * value + */ + private String value; + + public static SecurityAttribute none() { + return SecurityAttribute.builder().type("NONE").key("NONE").value("NONE").build(); + } +} diff --git a/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/decentral/DigitalTwinRegistryCreateShellService.java b/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/decentral/DigitalTwinRegistryCreateShellService.java new file mode 100644 index 0000000000..88b900558d --- /dev/null +++ b/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/decentral/DigitalTwinRegistryCreateShellService.java @@ -0,0 +1,71 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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.irs.registryclient.decentral; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.tractusx.irs.component.assetadministrationshell.AssetAdministrationShellDescriptor; +import org.eclipse.tractusx.irs.registryclient.decentral.exception.CreateDtrShellException; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +/** + * implementation of DigitalTwinRegistryService used to create shells in DTR + */ +@Slf4j +@RequiredArgsConstructor +public class DigitalTwinRegistryCreateShellService { + + private final RestTemplate restTemplate; + private final String createShellUrl; + + public AssetAdministrationShellDescriptor createShell( + final AssetAdministrationShellDescriptor assetAdministrationShellDescriptor) + throws CreateDtrShellException { + final ResponseEntity createdShellResponse; + try { + createdShellResponse = restTemplate.postForEntity(createShellUrl, + new HttpEntity<>(assetAdministrationShellDescriptor, headers()), + AssetAdministrationShellDescriptor.class); + final HttpStatusCode responseCode = createdShellResponse.getStatusCode(); + + if (responseCode.value() == HttpStatus.CREATED.value()) { + return createdShellResponse.getBody(); + } + } catch (RestClientException e) { + throw new CreateDtrShellException(e); + } + throw new CreateDtrShellException( + "Failed to create shell %s".formatted(assetAdministrationShellDescriptor.getGlobalAssetId())); + } + + private HttpHeaders headers() { + final HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + return headers; + } +} diff --git a/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/decentral/exception/CreateDtrShellException.java b/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/decentral/exception/CreateDtrShellException.java new file mode 100644 index 0000000000..15862e15fc --- /dev/null +++ b/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/decentral/exception/CreateDtrShellException.java @@ -0,0 +1,34 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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.irs.registryclient.decentral.exception; + +/** + * CreateDtrShellException used for create asset failure + */ +public class CreateDtrShellException extends Exception { + public CreateDtrShellException(final String message) { + super(message); + } + + public CreateDtrShellException(final Throwable cause) { + super(cause); + } +} diff --git a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DigitalTwinRegistryCreateShellServiceTest.java b/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DigitalTwinRegistryCreateShellServiceTest.java new file mode 100644 index 0000000000..89ae49c5fd --- /dev/null +++ b/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DigitalTwinRegistryCreateShellServiceTest.java @@ -0,0 +1,169 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 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.irs.registryclient.decentral; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.when; + +import java.util.List; + +import org.eclipse.tractusx.irs.component.assetadministrationshell.AssetAdministrationShellDescriptor; +import org.eclipse.tractusx.irs.component.assetadministrationshell.Endpoint; +import org.eclipse.tractusx.irs.component.assetadministrationshell.IdentifierKeyValuePair; +import org.eclipse.tractusx.irs.component.assetadministrationshell.ProtocolInformation; +import org.eclipse.tractusx.irs.component.assetadministrationshell.Reference; +import org.eclipse.tractusx.irs.component.assetadministrationshell.SecurityAttribute; +import org.eclipse.tractusx.irs.component.assetadministrationshell.SemanticId; +import org.eclipse.tractusx.irs.component.assetadministrationshell.SubmodelDescriptor; +import org.eclipse.tractusx.irs.registryclient.decentral.exception.CreateDtrShellException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +@ExtendWith(MockitoExtension.class) +class DigitalTwinRegistryCreateShellServiceTest { + + private static final String CREATE_SHELL_URL = "/shell-descriptors"; + @Mock + private RestTemplate restTemplate; + private DigitalTwinRegistryCreateShellService service; + + @BeforeEach + void setUp() { + service = new DigitalTwinRegistryCreateShellService(restTemplate, CREATE_SHELL_URL); + } + + @Test + void givenShell_whenCreateShell_thenCreteIt() throws CreateDtrShellException { + // given + AssetAdministrationShellDescriptor shell = testShell(); + when(restTemplate.postForEntity(any(String.class), any(HttpEntity.class), any())).thenReturn( + ResponseEntity.status(HttpStatus.CREATED).body(shell)); + + // when + AssetAdministrationShellDescriptor result = service.createShell(shell); + + // then + assertThat(result).isNotNull(); + } + + @Test + void givenShell_whenTemplateException_thenThrowException() { + // given + AssetAdministrationShellDescriptor shell = testShell(); + doThrow(new RestClientException("Surprise")).when(restTemplate) + .postForEntity(any(String.class), any(HttpEntity.class), any()); + + // when/then + assertThrows(CreateDtrShellException.class, () -> service.createShell(shell)); + } + + @Test + void givenShell_whenConflict_thenThrowException() { + // given + AssetAdministrationShellDescriptor shell = testShell(); + when(restTemplate.postForEntity(any(String.class), any(HttpEntity.class), any())).thenReturn( + ResponseEntity.status(HttpStatus.CONFLICT).body(shell)); + + // when/then + assertThrows(CreateDtrShellException.class, () -> service.createShell(shell)); + } + + @Test + void testCreateShell() throws CreateDtrShellException { + AssetAdministrationShellDescriptor shell = testShell(); + when(restTemplate.postForEntity(any(String.class), any(HttpEntity.class), any())).thenReturn( + ResponseEntity.status(HttpStatus.CREATED).body(shell)); + + // when + AssetAdministrationShellDescriptor result = service.createShell(shell); + + // then + assertThat(result).isNotNull(); + } + + private AssetAdministrationShellDescriptor testShell() { + return AssetAdministrationShellDescriptor.builder() + .globalAssetId("urn:uuid:254604ab-2153-45fb-8cad-54ef09f4080f") + .idShort("test") + .id("urn:uuid:25300562-aa66-4840-8952-5cef4ed667c2") + .specificAssetIds(List.of(IdentifierKeyValuePair.builder() + .name("manufacturerId") + .value("BPNL00000003CNKC") + .subjectId( + Reference.builder() + .keys(List.of( + SemanticId.builder() + .type("GlobalReference") + .value("PUBLIC_READABLE") + .build())) + .build()) + .build())) + .submodelDescriptors(List.of(SubmodelDescriptor.builder() + .idShort( + "SingleLevelUsageAsBuilt") + .id("urn:uuid:e401ccb7-a8f5-499a-9340-93746822d775") + .semanticId( + Reference.builder() + .type("ExternalReference") + .keys(List.of( + SemanticId.builder() + .type("GlobalReference") + .value("urn:bamm:io.catenax.single_level_usage_as_built:2.0.0#SingleLevelUsageAsBuilt") + .build())) + .build() + + ) + .endpoints( + List.of(Endpoint.builder() + .interfaceInformation( + "SUBMODEL-3.0") + .protocolInformation( + ProtocolInformation.builder() + .href("https://trace-x-edc-dataplane.dev.demo.catena-x.net/api/public/data/urn:uuid:e401ccb7-a8f5-499a-9340-93746822d775") + .endpointProtocol( + "HTTP") + .endpointProtocolVersion( + List.of("1.1")) + .subprotocol( + "DSP") + .subprotocolBody( + "id=urn:uuid:cb6d86b5-b8ea-4fc0-b10b-ff2dd62f793d;dspEndpoint=https://trace-x-edc.dev.demo.catena-x.net") + .subprotocolBodyEncoding( + "plain") + .securityAttributes( + List.of(SecurityAttribute.none())) + + .build()) + .build())) + .build())) + .build(); + } +} \ No newline at end of file