diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/Readme.md b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/Readme.md new file mode 100644 index 000000000..c133b7417 --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/Readme.md @@ -0,0 +1,19 @@ +# AssetAdministrationShell Repository - Registry Integration +This feature automatically integrates the Descriptor with the Registry while creation of the Shell at Repository.
+It also automatically removes the Descriptor from the Registry when the Shell is removed from the Repository. + +To enable this feature, the following two properties should be configured: + +``` +basyx.aasrepository.feature.registryintegration = {AAS-Registry-Base-Url} +basyx.externalurl = {AAS-Repo-Base-Url} +``` + +This feature gets enabled automatically when both of the above defined properties are configured, i.e., no external enabled/disabled property is required. + +An example valid configuration: + +``` +basyx.aasrepository.feature.registryintegration = http://localhost:8050/api/v3.0 +basyx.externalurl = http://localhost:8081 +``` diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/pom.xml b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/pom.xml new file mode 100644 index 000000000..b331ff60f --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/pom.xml @@ -0,0 +1,45 @@ + + 4.0.0 + + + org.eclipse.digitaltwin.basyx + basyx.aasrepository + ${revision} + + + basyx.aasrepository-feature-registry-integration + + + + org.eclipse.digitaltwin.basyx + basyx.aasregistry-client-native + + + org.eclipse.digitaltwin.basyx + basyx.aasrepository-core + + + org.eclipse.digitaltwin.basyx + basyx.http + test + tests + + + org.eclipse.digitaltwin.basyx + basyx.http + + + org.eclipse.digitaltwin.basyx + basyx.aasrepository-http + tests + test + + + org.apache.httpcomponents.client5 + httpclient5 + test + + + diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasDescriptorFactory.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasDescriptorFactory.java new file mode 100644 index 000000000..92bf90d47 --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasDescriptorFactory.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; + +import org.eclipse.digitaltwin.aas4j.v3.model.AdministrativeInformation; +import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; +import org.eclipse.digitaltwin.aas4j.v3.model.AssetInformation; +import org.eclipse.digitaltwin.aas4j.v3.model.Extension; +import org.eclipse.digitaltwin.aas4j.v3.model.LangStringNameType; +import org.eclipse.digitaltwin.aas4j.v3.model.LangStringTextType; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.Endpoint; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.ProtocolInformation; +import org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration.mapper.AttributeMapper; +import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; + +/** + * Factory for creating the {@link AssetAdministrationShellDescriptor} + * + * @author danish + */ +public class AasDescriptorFactory { + + private static final String AAS_INTERFACE = "AAS-3.0"; + private static final String AAS_REPOSITORY_PATH = "/shells"; + + private AssetAdministrationShell shell; + private String aasRepositoryURL; + + private AttributeMapper attributeMapper; + + public AasDescriptorFactory(AssetAdministrationShell shell, String aasRepositoryBaseURL, AttributeMapper attributeMapper) { + super(); + this.shell = shell; + this.aasRepositoryURL = createAasRepositoryUrl(aasRepositoryBaseURL); + this.attributeMapper = attributeMapper; + } + + /** + * Creates {@link AssetAdministrationShellDescriptor} + * + * @return the created AssetAdministrationShellDescriptor + */ + public AssetAdministrationShellDescriptor create() { + + AssetAdministrationShellDescriptor descriptor = new AssetAdministrationShellDescriptor(); + + setId(shell.getId(), descriptor); + + setIdShort(shell.getIdShort(), descriptor); + + setEndpointItem(shell.getId(), descriptor); + + setDescription(shell.getDescription(), descriptor); + + setDisplayName(shell.getDisplayName(), descriptor); + + setExtensions(shell.getExtensions(), descriptor); + + setAdministration(shell.getAdministration(), descriptor); + + setAssetKind(shell.getAssetInformation(), descriptor); + + setAssetType(shell.getAssetInformation(), descriptor); + + setGlobalAssetId(shell.getAssetInformation(), descriptor); + + return descriptor; + } + + private void setDescription(List descriptions, AssetAdministrationShellDescriptor descriptor) { + + if (descriptions == null || descriptions.isEmpty()) + return; + + descriptor.setDescription(attributeMapper.mapDescription(descriptions)); + } + + private void setDisplayName(List displayNames, AssetAdministrationShellDescriptor descriptor) { + + if (displayNames == null || displayNames.isEmpty()) + return; + + descriptor.setDisplayName(attributeMapper.mapDisplayName(displayNames)); + } + + private void setExtensions(List extensions, AssetAdministrationShellDescriptor descriptor) { + + if (extensions == null || extensions.isEmpty()) + return; + + descriptor.setExtensions(attributeMapper.mapExtensions(extensions)); + } + + private void setAdministration(AdministrativeInformation administration, AssetAdministrationShellDescriptor descriptor) { + + if (administration == null) + return; + + descriptor.setAdministration(attributeMapper.mapAdministration(administration)); + } + + private void setAssetKind(AssetInformation assetInformation, AssetAdministrationShellDescriptor descriptor) { + + if (assetInformation == null || assetInformation.getAssetKind() == null) + return; + + descriptor.setAssetKind(attributeMapper.mapAssetKind(assetInformation.getAssetKind())); + } + + private void setAssetType(AssetInformation assetInformation, AssetAdministrationShellDescriptor descriptor) { + + if (assetInformation == null || assetInformation.getAssetType() == null) + return; + + descriptor.setAssetType(assetInformation.getAssetType()); + } + + private void setGlobalAssetId(AssetInformation assetInformation, AssetAdministrationShellDescriptor descriptor) { + + if (assetInformation == null || assetInformation.getGlobalAssetId() == null) + return; + + descriptor.setGlobalAssetId(assetInformation.getGlobalAssetId()); + } + + private void setEndpointItem(String shellId, AssetAdministrationShellDescriptor descriptor) { + + Endpoint endpoint = new Endpoint(); + endpoint.setInterface(AAS_INTERFACE); + ProtocolInformation protocolInformation = createProtocolInformation(shellId); + endpoint.setProtocolInformation(protocolInformation); + + descriptor.addEndpointsItem(endpoint); + } + + private ProtocolInformation createProtocolInformation(String shellId) { + String href = String.format("%s/%s", aasRepositoryURL, Base64UrlEncodedIdentifier.encodeIdentifier(shellId)); + + ProtocolInformation protocolInformation = new ProtocolInformation(); + protocolInformation.endpointProtocol(getProtocol(href)); + protocolInformation.setHref(href); + + return protocolInformation; + } + + private void setIdShort(String idShort, AssetAdministrationShellDescriptor descriptor) { + descriptor.setIdShort(idShort); + } + + private void setId(String shellId, AssetAdministrationShellDescriptor descriptor) { + descriptor.setId(shellId); + } + + private String getProtocol(String endpoint) { + try { + return new URL(endpoint).getProtocol(); + } catch (MalformedURLException e) { + throw new RuntimeException(); + } + } + + private String createAasRepositoryUrl(String aasRepositoryBaseURL) { + + try { + return new URL(new URL(aasRepositoryBaseURL), AAS_REPOSITORY_PATH).toString(); + } catch (MalformedURLException e) { + throw new RuntimeException("The AAS Repository Base url is malformed.\n" + e.getMessage()); + } + } + +} diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLink.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLink.java new file mode 100644 index 000000000..9210e24cd --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLink.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration; + +import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi; +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; + +/** + * Represents information for linking {@link AasRepository} with AasRegistry + * + * @author danish + */ +public class AasRepositoryRegistryLink { + + private RegistryAndDiscoveryInterfaceApi registryApi; + private String aasRepositoryBaseURL; + + public AasRepositoryRegistryLink(RegistryAndDiscoveryInterfaceApi registryApi, String aasRepositoryBaseURL) { + super(); + this.registryApi = registryApi; + this.aasRepositoryBaseURL = aasRepositoryBaseURL; + } + + public RegistryAndDiscoveryInterfaceApi getRegistryApi() { + return registryApi; + } + + public String getAasRepositoryBaseURL() { + return aasRepositoryBaseURL; + } + +} diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepository.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepository.java new file mode 100644 index 000000000..18c73a895 --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepository.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (C) 2021 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ +package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration; + +import java.util.List; + +import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; +import org.eclipse.digitaltwin.aas4j.v3.model.AssetInformation; +import org.eclipse.digitaltwin.aas4j.v3.model.Reference; +import org.eclipse.digitaltwin.basyx.aasregistry.client.ApiException; +import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor; +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; +import org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration.mapper.AttributeMapper; +import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException; +import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; +import org.eclipse.digitaltwin.basyx.core.exceptions.RepositoryRegistryLinkException; +import org.eclipse.digitaltwin.basyx.core.exceptions.RepositoryRegistryUnlinkException; +import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; +import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Decorator for linking {@link AasRepository} with AasRegistry + * + * @author danish + * + */ +public class RegistryIntegrationAasRepository implements AasRepository { + private static Logger logger = LoggerFactory.getLogger(RegistryIntegrationAasRepository.class); + + private AasRepository decorated; + + private AasRepositoryRegistryLink aasRepositoryRegistryLink; + private AttributeMapper attributeMapper; + + public RegistryIntegrationAasRepository(AasRepository decorated, AasRepositoryRegistryLink aasRepositoryRegistryLink, AttributeMapper attributeMapper) { + this.decorated = decorated; + this.aasRepositoryRegistryLink = aasRepositoryRegistryLink; + this.attributeMapper = attributeMapper; + } + + @Override + public CursorResult> getAllAas(PaginationInfo pInfo) { + return decorated.getAllAas(pInfo); + } + + @Override + public AssetAdministrationShell getAas(String shellId) throws ElementDoesNotExistException { + return decorated.getAas(shellId); + } + + @Override + public void createAas(AssetAdministrationShell shell) throws CollidingIdentifierException { + decorated.createAas(shell); + + integrateAasWithRegistry(shell, aasRepositoryRegistryLink.getAasRepositoryBaseURL()); + } + + @Override + public void updateAas(String shellId, AssetAdministrationShell shell) { + decorated.updateAas(shellId, shell); + } + + @Override + public void deleteAas(String shellId) { + deleteFromRegistry(shellId); + + decorated.deleteAas(shellId); + } + + @Override + public String getName() { + return decorated.getName(); + } + + @Override + public CursorResult> getSubmodelReferences(String shellId, PaginationInfo paginationInfo) { + return decorated.getSubmodelReferences(shellId, paginationInfo); + } + + @Override + public void addSubmodelReference(String shellId, Reference submodelReference) { + decorated.addSubmodelReference(shellId, submodelReference); + } + + @Override + public void removeSubmodelReference(String shellId, String submodelId) { + decorated.removeSubmodelReference(shellId, submodelId); + } + + @Override + public void setAssetInformation(String shellId, AssetInformation shellInfo) throws ElementDoesNotExistException { + decorated.setAssetInformation(shellId, shellInfo); + } + + @Override + public AssetInformation getAssetInformation(String shellId) throws ElementDoesNotExistException { + return decorated.getAssetInformation(shellId); + } + + private void integrateAasWithRegistry(AssetAdministrationShell shell, String aasRepositoryURL) { + AssetAdministrationShellDescriptor descriptor = new AasDescriptorFactory(shell, aasRepositoryURL, attributeMapper).create(); + + RegistryAndDiscoveryInterfaceApi registryApi = aasRepositoryRegistryLink.getRegistryApi(); + + try { + registryApi.postAssetAdministrationShellDescriptor(descriptor); + + logger.info("Shell '{}' has been automatically linked with the Registry", shell.getId()); + } catch (ApiException e) { + e.printStackTrace(); + + throw new RepositoryRegistryLinkException(shell.getId()); + } + } + + private void deleteFromRegistry(String shellId) { + RegistryAndDiscoveryInterfaceApi registryApi = aasRepositoryRegistryLink.getRegistryApi(); + + if (!shellExistsOnRegistry(shellId, registryApi)) { + logger.error("Unable to un-link the AAS descriptor '{}' from the Registry because it does not exist on the Registry.", shellId); + + return; + } + + try { + registryApi.deleteAssetAdministrationShellDescriptorById(shellId); + + logger.info("Shell '{}' has been automatically un-linked from the Registry.", shellId); + } catch (ApiException e) { + e.printStackTrace(); + + throw new RepositoryRegistryUnlinkException(shellId); + } + } + + private boolean shellExistsOnRegistry(String shellId, RegistryAndDiscoveryInterfaceApi registryApi) { + try { + registryApi.getAssetAdministrationShellDescriptorById(shellId); + + return true; + } catch (ApiException e) { + return false; + } + } + +} diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepositoryConfiguration.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepositoryConfiguration.java new file mode 100644 index 000000000..c4700498b --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepositoryConfiguration.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + + +package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration; + +import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi; +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; +import org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration.mapper.AttributeMapper; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Configuration for integrating {@link AasRepository} with AasRegistry + * + * @author danish + */ +@Configuration +@ConditionalOnExpression("!T(org.springframework.util.StringUtils).isEmpty('${basyx.aasrepository.feature.registryintegration:}') && !T(org.springframework.util.StringUtils).isEmpty('${basyx.externalurl:}')") +public class RegistryIntegrationAasRepositoryConfiguration { + + @Bean + @ConditionalOnMissingBean + public AasRepositoryRegistryLink getAasRepositoryRegistryLink(@Value("${basyx.aasrepository.feature.registryintegration}") String registryBasePath, @Value("${basyx.externalurl}") String aasRepositoryBaseURL) { + + return new AasRepositoryRegistryLink(new RegistryAndDiscoveryInterfaceApi(registryBasePath), aasRepositoryBaseURL); + } + + @Bean + @ConditionalOnMissingBean + public AttributeMapper getAasAttributeMapper(ObjectMapper objectMapper) { + + return new AttributeMapper(objectMapper); + } + +} diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepositoryFactory.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepositoryFactory.java new file mode 100644 index 000000000..4ef1d37dd --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepositoryFactory.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration; + +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepositoryFactory; +import org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration.mapper.AttributeMapper; + +/** + * Factory for creating {@link RegistryIntegrationAasRepository} + * + * @author danish + */ +public class RegistryIntegrationAasRepositoryFactory implements AasRepositoryFactory { + + private AasRepositoryFactory decorated; + private AasRepositoryRegistryLink aasRepositoryRegistryLink; + private AttributeMapper attributeMapper; + + public RegistryIntegrationAasRepositoryFactory(AasRepositoryFactory decorated, AasRepositoryRegistryLink aasRepositoryRegistryLink, AttributeMapper attributeMapper) { + this.decorated = decorated; + this.aasRepositoryRegistryLink = aasRepositoryRegistryLink; + this.attributeMapper = attributeMapper; + } + + @Override + public AasRepository create() { + return new RegistryIntegrationAasRepository(decorated.create(), aasRepositoryRegistryLink, attributeMapper); + } + +} diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepositoryFeature.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepositoryFeature.java new file mode 100644 index 000000000..0023d0b59 --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepositoryFeature.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + + +package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration; + +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepositoryFactory; +import org.eclipse.digitaltwin.basyx.aasrepository.feature.AasRepositoryFeature; +import org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration.mapper.AttributeMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +/** + * Feature for integrating Registry with {@link AasRepository} + * + * @author danish + */ +@Component +@ConditionalOnExpression("!T(org.springframework.util.StringUtils).isEmpty('${basyx.aasrepository.feature.registryintegration:}') && !T(org.springframework.util.StringUtils).isEmpty('${basyx.externalurl:}')") +public class RegistryIntegrationAasRepositoryFeature implements AasRepositoryFeature { + public final static String FEATURENAME = "basyx.aasrepository.feature.registryintegration"; + + private AasRepositoryRegistryLink aasRepositoryRegistryLink; + + @Value("${" + FEATURENAME + ":}") + private String registryBaseURL; + + @Value("${basyx.externalurl:}") + private String aasRepositoryExternalBaseURL; + + private AttributeMapper attributeMapper; + + @Autowired + public RegistryIntegrationAasRepositoryFeature(AasRepositoryRegistryLink aasRepositoryRegistryLink, AttributeMapper attributeMapper) { + this.aasRepositoryRegistryLink = aasRepositoryRegistryLink; + this.attributeMapper = attributeMapper; + } + + @Override + public AasRepositoryFactory decorate(AasRepositoryFactory aasRepositoryFactory) { + return new RegistryIntegrationAasRepositoryFactory(aasRepositoryFactory, aasRepositoryRegistryLink, attributeMapper); + } + + @Override + public void initialize() { + } + + @Override + public void cleanUp() { + + } + + @Override + public String getName() { + return "AasRepository Registry Integration"; + } + + @Override + public boolean isEnabled() { + return !registryBaseURL.isBlank() && !aasRepositoryExternalBaseURL.isBlank(); + } +} diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/mapper/AttributeMapper.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/mapper/AttributeMapper.java new file mode 100644 index 000000000..f2352601a --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/mapper/AttributeMapper.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration.mapper; + +import java.util.List; + +import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; +import org.eclipse.digitaltwin.aas4j.v3.model.AssetInformation; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AdministrativeInformation; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetKind; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.Extension; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.LangStringNameType; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.LangStringTextType; +import org.eclipse.digitaltwin.basyx.http.CustomTypeCloneFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Maps the models defined in AAS4J to the AasRegistry client models + * + * @author danish + */ +public class AttributeMapper { + + private Logger logger = LoggerFactory.getLogger(AttributeMapper.class); + + private ObjectMapper mapper; + + public AttributeMapper(ObjectMapper mapper) { + this.mapper = mapper; + } + + /** + * Maps {@link AssetAdministrationShell#getDescription()} from AAS4J to + * AasRegistry client + * + * @param descriptions + * @return the mapped descriptions + */ + public List mapDescription(List descriptions) { + CustomTypeCloneFactory cloneFactory = new CustomTypeCloneFactory<>(LangStringTextType.class, mapper); + + List mappedDescriptions = cloneFactory.create(descriptions); + + if (mappedDescriptions == null) + logger.error("Descriptions could not be mapped due to a failure."); + + return mappedDescriptions; + } + + /** + * Maps {@link AssetAdministrationShell#getDisplayName()} from AAS4J to + * AasRegistry client + * + * @param displayNames + * @return the mapped displayNames + */ + public List mapDisplayName(List displayNames) { + CustomTypeCloneFactory cloneFactory = new CustomTypeCloneFactory<>(LangStringNameType.class, mapper); + + List mappedDisplayNames = cloneFactory.create(displayNames); + + if (mappedDisplayNames == null) + logger.error("DisplayNames could not be mapped due to a failure."); + + return mappedDisplayNames; + } + + /** + * Maps {@link AssetAdministrationShell#getExtensions()} from AAS4J to + * AasRegistry client + * + * @param extensions + * @return the mapped extensions + */ + public List mapExtensions(List extensions) { + CustomTypeCloneFactory cloneFactory = new CustomTypeCloneFactory<>(Extension.class, mapper); + + List mappedExtensions = cloneFactory.create(extensions); + + if (mappedExtensions == null) + logger.error("Extensions could not be mapped due to a failure."); + + return cloneFactory.create(extensions); + } + + /** + * Maps {@link AssetAdministrationShell#getAdministration()} from AAS4J to + * AasRegistry client + * + * @param administrativeInformation + * @return the mapped administrativeInformation + */ + public AdministrativeInformation mapAdministration(org.eclipse.digitaltwin.aas4j.v3.model.AdministrativeInformation administrativeInformation) { + CustomTypeCloneFactory cloneFactory = new CustomTypeCloneFactory<>(AdministrativeInformation.class, mapper); + + AdministrativeInformation mappedAdministrativeInformation = cloneFactory.create(administrativeInformation); + + if (mappedAdministrativeInformation == null) + logger.error("AdministrativeInformation could not be mapped due to a failure."); + + return mappedAdministrativeInformation; + } + + /** + * Maps {@link AssetInformation#getAssetKind()} from AAS4J to AasRegistry client + * + * @param assetKind + * @return the mapped assetKind + */ + public AssetKind mapAssetKind(org.eclipse.digitaltwin.aas4j.v3.model.AssetKind assetKind) { + + return AssetKind.valueOf(AssetKind.class, assetKind.name()); + } + +} diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryTestLink.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryTestLink.java new file mode 100644 index 000000000..521cec341 --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryTestLink.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; + +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.eclipse.digitaltwin.basyx.aasregistry.client.ApiException; +import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetKind; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.Endpoint; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.GetAssetAdministrationShellDescriptorsResult; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.ProtocolInformation; +import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; +import org.eclipse.digitaltwin.basyx.http.serialization.BaSyxHttpTestUtils; +import org.junit.Test; +import org.springframework.http.HttpStatus; + +/** + * Integration test for {@link RegistryIntegrationAasRepository} feature + * + * @author danish + */ +public class AasRepositoryRegistryTestLink { + + private static final String AAS_REPOSITORY_PATH = "/shells"; + private static final String DUMMY_GLOBAL_ASSETID = "globalAssetId"; + private static final String DUMMY_IDSHORT = "ExampleMotor"; + private static final String DUMMY_AAS_ID = "customIdentifier"; + + public static String aasRepoBaseUrl = "http://localhost:8081"; + public static String aasRegistryUrl = "http://localhost:8050/api/v3.0"; + + private static final AssetAdministrationShellDescriptor DUMMY_DESCRIPTOR = createExpectedDescriptor(); + + @Test + public void createAas() throws FileNotFoundException, IOException, ApiException { + String aasJsonContent = getAas1JSONString(); + + CloseableHttpResponse creationResponse = createAasOnRepo(aasJsonContent); + assertEquals(HttpStatus.CREATED.value(), creationResponse.getCode()); + + AssetAdministrationShellDescriptor actualDescriptor = retrieveDescriptorFromRegistry(); + + assertEquals(DUMMY_DESCRIPTOR, actualDescriptor); + + resetRepository(); + } + + @Test + public void deleteAas() throws FileNotFoundException, IOException, ApiException { + String aasJsonContent = getAas1JSONString(); + + CloseableHttpResponse creationResponse = createAasOnRepo(aasJsonContent); + assertEquals(HttpStatus.CREATED.value(), creationResponse.getCode()); + + CloseableHttpResponse deleteResponse = deleteAasFromRepo(DUMMY_AAS_ID); + assertEquals(HttpStatus.NO_CONTENT.value(), deleteResponse.getCode()); + + assertDescriptionDeletionAtRegistry(); + } + + private AssetAdministrationShellDescriptor retrieveDescriptorFromRegistry() throws ApiException { + RegistryAndDiscoveryInterfaceApi api = new RegistryAndDiscoveryInterfaceApi(aasRegistryUrl); + + return api.getAssetAdministrationShellDescriptorById(DUMMY_AAS_ID); + } + + private void resetRepository() throws IOException { + CloseableHttpResponse deleteResponse = deleteAasFromRepo(DUMMY_AAS_ID); + + assertEquals(HttpStatus.NO_CONTENT.value(), deleteResponse.getCode()); + } + + private CloseableHttpResponse deleteAasFromRepo(String shellId) throws IOException { + return BaSyxHttpTestUtils.executeDeleteOnURL(getSpecificAasAccessURL(shellId)); + } + + private void assertDescriptionDeletionAtRegistry() throws ApiException { + RegistryAndDiscoveryInterfaceApi api = new RegistryAndDiscoveryInterfaceApi(aasRegistryUrl); + + GetAssetAdministrationShellDescriptorsResult result = api.getAllAssetAdministrationShellDescriptors(null, null, null, null); + + List actualDescriptors = result.getResult(); + + assertTrue(actualDescriptors.isEmpty()); + } + + private String getAas1JSONString() throws FileNotFoundException, IOException { + return BaSyxHttpTestUtils.readJSONStringFromClasspath("AasSimple_1.json"); + } + + private CloseableHttpResponse createAasOnRepo(String aasJsonContent) throws IOException { + return BaSyxHttpTestUtils.executePostOnURL(createAasRepositoryUrl(aasRepoBaseUrl), aasJsonContent); + } + + private String getSpecificAasAccessURL(String aasId) { + return createAasRepositoryUrl(aasRepoBaseUrl) + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(aasId); + } + + private static AssetAdministrationShellDescriptor createExpectedDescriptor() { + + AssetAdministrationShellDescriptor descriptor = new AssetAdministrationShellDescriptor(); + + descriptor.setId(DUMMY_AAS_ID); + descriptor.setIdShort(DUMMY_IDSHORT); + descriptor.setAssetKind(AssetKind.INSTANCE); + descriptor.setGlobalAssetId(DUMMY_GLOBAL_ASSETID); + descriptor.addEndpointsItem(createEndpointItem()); + + return descriptor; + } + + private static Endpoint createEndpointItem() { + Endpoint endpoint = new Endpoint(); + endpoint.setInterface("AAS-3.0"); + endpoint.setProtocolInformation(createProtocolInformation()); + + return endpoint; + } + + private static ProtocolInformation createProtocolInformation() { + String href = createHref(); + + ProtocolInformation protocolInformation = new ProtocolInformation(); + protocolInformation.setHref(href); + protocolInformation.endpointProtocol(getProtocol(href)); + + return protocolInformation; + } + + private static String createHref() { + return String.format("%s/%s", createAasRepositoryUrl(aasRepoBaseUrl), Base64UrlEncodedIdentifier.encodeIdentifier(DUMMY_AAS_ID)); + } + + private static String getProtocol(String endpoint) { + try { + return new URL(endpoint).getProtocol(); + } catch (MalformedURLException e) { + throw new RuntimeException(); + } + } + + private static String createAasRepositoryUrl(String aasRepositoryBaseURL) { + + try { + return new URL(new URL(aasRepositoryBaseURL), AAS_REPOSITORY_PATH).toString(); + } catch (MalformedURLException e) { + throw new RuntimeException("The AAS Repository Base url is malformed. " + e.getMessage()); + } + } + +} diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryTestLinkApplication.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryTestLinkApplication.java new file mode 100644 index 000000000..7dbd91dec --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryTestLinkApplication.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ +package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration; + +import org.junit.internal.TextListener; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; + +/** + * Application for testing the {@link RegistryIntegrationAasRepository} feature. + * The first argument is the AAS Repository Base URL, the second argument is the AAS + * Registry URL. + * + * @author schnicke, danish + * + */ +public class AasRepositoryRegistryTestLinkApplication { + + public static void main(String[] args) throws Exception { + String aasRepoBaseUrl = getAasRepositoryBaseUrl(args); + String aasRegUrl = getAasRegistryUrl(args); + + Result result = runTests(aasRepoBaseUrl, aasRegUrl); + + printResults(result); + } + + private static void printResults(Result result) { + System.out.println("Finished. Result: Failures: " + result.getFailureCount() + ". Ignored: " + result.getIgnoreCount() + ". Tests run: " + result.getRunCount() + ". Time: " + result.getRunTime() + "ms."); + } + + private static Result runTests(String aasRepoBaseUrl, String aasRegUrl) { + AasRepositoryRegistryTestLink.aasRepoBaseUrl = aasRepoBaseUrl; + AasRepositoryRegistryTestLink.aasRegistryUrl = aasRegUrl; + + JUnitCore junit = new JUnitCore(); + junit.addListener(new TextListener(System.out)); + + return junit.run(AasRepositoryRegistryTestLink.class); + } + + private static String getAasRepositoryBaseUrl(String[] args) { + return args[0]; + } + + private static String getAasRegistryUrl(String[] args) { + return args[1]; + } + +} diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationTestHelper.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationTestHelper.java new file mode 100644 index 000000000..91b5bf930 --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationTestHelper.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.digitaltwin.aas4j.v3.model.AdministrativeInformation; +import org.eclipse.digitaltwin.aas4j.v3.model.AssetKind; +import org.eclipse.digitaltwin.aas4j.v3.model.EmbeddedDataSpecification; +import org.eclipse.digitaltwin.aas4j.v3.model.Extension; +import org.eclipse.digitaltwin.aas4j.v3.model.KeyTypes; +import org.eclipse.digitaltwin.aas4j.v3.model.LangStringNameType; +import org.eclipse.digitaltwin.aas4j.v3.model.LangStringTextType; +import org.eclipse.digitaltwin.aas4j.v3.model.Reference; +import org.eclipse.digitaltwin.aas4j.v3.model.ReferenceTypes; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultAdministrativeInformation; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultEmbeddedDataSpecification; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultExtension; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultKey; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultLangStringNameType; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultLangStringTextType; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultReference; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.DataTypeDefXsd; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.Key; + +/** + * A helper class for testing RegistryIntegration feature + * + * @author danish + */ +public class RegistryIntegrationTestHelper { + + // LangStringTextType AAS4J + private static final LangStringTextType AAS4J_LANG_STRING_TEXT_TYPE_1 = new DefaultLangStringTextType.Builder().language("de").text("Ein Beispiel").build(); + private static final LangStringTextType AAS_LANG_STRING_TEXT_TYPE_2 = new DefaultLangStringTextType.Builder().language("en").text("An Example").build(); + + // LangStringTextType AasRegistry + private static final org.eclipse.digitaltwin.basyx.aasregistry.client.model.LangStringTextType AAS_REG_LANG_STRING_TEXT_TYPE_1 = new org.eclipse.digitaltwin.basyx.aasregistry.client.model.LangStringTextType().language("de") + .text("Ein Beispiel"); + private static final org.eclipse.digitaltwin.basyx.aasregistry.client.model.LangStringTextType AAS_REG_LANG_STRING_TEXT_TYPE_2 = new org.eclipse.digitaltwin.basyx.aasregistry.client.model.LangStringTextType().language("en") + .text("An Example"); + + // LangStringNameType AAS4J + private static final LangStringNameType AAS4J_LANG_STRING_NAME_TYPE_1 = new DefaultLangStringNameType.Builder().language("en").text("Name type string").build(); + private static final LangStringNameType AAS4J_LANG_STRING_NAME_TYPE_2 = new DefaultLangStringNameType.Builder().language("de").text("Namenstypzeichenfolge").build(); + + // LangStringNameType AasRegistry + private static final org.eclipse.digitaltwin.basyx.aasregistry.client.model.LangStringNameType AAS_REG_LANG_STRING_NAME_TYPE_1 = new org.eclipse.digitaltwin.basyx.aasregistry.client.model.LangStringNameType().language("en") + .text("Name type string"); + private static final org.eclipse.digitaltwin.basyx.aasregistry.client.model.LangStringNameType AAS_REG_LANG_STRING_NAME_TYPE_2 = new org.eclipse.digitaltwin.basyx.aasregistry.client.model.LangStringNameType().language("de") + .text("Namenstypzeichenfolge"); + + // AssetKind AAS4J + public static final AssetKind AAS4J_ASSET_KIND = AssetKind.INSTANCE; + + // AssetKind AasRegistry + public static final org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetKind AASREG_ASSET_KIND = org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetKind.INSTANCE; + + // Administration AAS4J + private static final Reference AAS4J_DATASPECIFICATION = new DefaultReference.Builder().keys(new DefaultKey.Builder().type(KeyTypes.BLOB).value("BlobValue").build()).type(ReferenceTypes.EXTERNAL_REFERENCE).build(); + private static final EmbeddedDataSpecification AAS4JEMB_EMBEDDED_DATA_SPECIFICATION = new DefaultEmbeddedDataSpecification.Builder().dataSpecification(AAS4J_DATASPECIFICATION).build(); + + // Administration AasRegistry + private static final org.eclipse.digitaltwin.basyx.aasregistry.client.model.Reference AAS_REG_DATASPECIFICATION = new org.eclipse.digitaltwin.basyx.aasregistry.client.model.Reference() + .keys(Arrays.asList(new Key().type(org.eclipse.digitaltwin.basyx.aasregistry.client.model.KeyTypes.BLOB).value("BlobValue"))).type(org.eclipse.digitaltwin.basyx.aasregistry.client.model.ReferenceTypes.EXTERNALREFERENCE); + private static final org.eclipse.digitaltwin.basyx.aasregistry.client.model.EmbeddedDataSpecification AAS_REG_EMBEDDED_DATA_SPECIFICATION = new org.eclipse.digitaltwin.basyx.aasregistry.client.model.EmbeddedDataSpecification() + .dataSpecification(AAS_REG_DATASPECIFICATION); + + private static final String VERSION = "1.0.0"; + private static final String REVISION = "3"; + private static final String TEMPLATE_ID = "ID2.0"; + + // Extension AAS4J + private static final Extension AAS4J_EXTENSION = new DefaultExtension.Builder().semanticId(AAS4J_DATASPECIFICATION).name("extension").valueType(org.eclipse.digitaltwin.aas4j.v3.model.DataTypeDefXsd.STRING).value("extensionValue").build(); + + // Extension AasRegistry + private static final org.eclipse.digitaltwin.basyx.aasregistry.client.model.Extension AAS_REG_EXTENSION = new org.eclipse.digitaltwin.basyx.aasregistry.client.model.Extension().semanticId(AAS_REG_DATASPECIFICATION).name("extension") + .valueType(DataTypeDefXsd.STRING).value("extensionValue"); + + public static List getAas4jLangStringTextTypes() { + return Arrays.asList(AAS4J_LANG_STRING_TEXT_TYPE_1, AAS_LANG_STRING_TEXT_TYPE_2); + } + + public static List getAasRegLangStringTextTypes() { + return Arrays.asList(AAS_REG_LANG_STRING_TEXT_TYPE_1, AAS_REG_LANG_STRING_TEXT_TYPE_2); + } + + public static AdministrativeInformation getAas4jAdministration() { + return new DefaultAdministrativeInformation.Builder().embeddedDataSpecifications(AAS4JEMB_EMBEDDED_DATA_SPECIFICATION).version(VERSION).revision(REVISION).templateId(TEMPLATE_ID).build(); + } + + public static org.eclipse.digitaltwin.basyx.aasregistry.client.model.AdministrativeInformation getAasRegAdministration() { + return new org.eclipse.digitaltwin.basyx.aasregistry.client.model.AdministrativeInformation().embeddedDataSpecifications(Arrays.asList(AAS_REG_EMBEDDED_DATA_SPECIFICATION)).version(VERSION).revision(REVISION) + .templateId(TEMPLATE_ID); + } + + public static List getAas4jLangStringNameTypes() { + return Arrays.asList(AAS4J_LANG_STRING_NAME_TYPE_1, AAS4J_LANG_STRING_NAME_TYPE_2); + } + + public static List getAasRegLangStringNameTypes() { + return Arrays.asList(AAS_REG_LANG_STRING_NAME_TYPE_1, AAS_REG_LANG_STRING_NAME_TYPE_2); + } + + public static List getAas4jExtensions() { + return Arrays.asList(AAS4J_EXTENSION); + } + + public static List getAasRegExtensions() { + return Arrays.asList(AAS_REG_EXTENSION); + } + +} diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/TestAttributeMapper.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/TestAttributeMapper.java new file mode 100644 index 000000000..6730fec19 --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/TestAttributeMapper.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AdministrativeInformation; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetKind; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.Extension; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.LangStringNameType; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.LangStringTextType; +import org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration.mapper.AttributeMapper; +import org.eclipse.digitaltwin.basyx.http.Aas4JHTTPSerializationExtension; +import org.eclipse.digitaltwin.basyx.http.BaSyxHTTPConfiguration; +import org.eclipse.digitaltwin.basyx.http.SerializationExtension; +import org.junit.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Unit tests for {@link AttributeMapper} + * + * @author danish + */ +public class TestAttributeMapper { + + private static AttributeMapper attributeMapper = new AttributeMapper(configureObjectMapper()); + + @Test + public void mapDescriptions() { + List expectedDescriptions = RegistryIntegrationTestHelper.getAasRegLangStringTextTypes(); + + List actualDescriptions = attributeMapper.mapDescription(RegistryIntegrationTestHelper.getAas4jLangStringTextTypes()); + + assertEquals(expectedDescriptions.size(), actualDescriptions.size()); + assertEquals(expectedDescriptions, actualDescriptions); + } + + @Test + public void mapDisplayNames() { + List expectedDisplayNames = RegistryIntegrationTestHelper.getAasRegLangStringNameTypes(); + + List actualDisplayNames = attributeMapper.mapDisplayName(RegistryIntegrationTestHelper.getAas4jLangStringNameTypes()); + + assertEquals(expectedDisplayNames.size(), actualDisplayNames.size()); + assertEquals(expectedDisplayNames, actualDisplayNames); + } + + @Test + public void mapExtensions() { + List expectedExtensions = RegistryIntegrationTestHelper.getAasRegExtensions(); + + List actualExtensions = attributeMapper.mapExtensions(RegistryIntegrationTestHelper.getAas4jExtensions()); + + assertEquals(expectedExtensions.size(), actualExtensions.size()); + assertEquals(expectedExtensions, actualExtensions); + } + + @Test + public void mapAdministration() { + AdministrativeInformation expectedAdministrativeInformation = RegistryIntegrationTestHelper.getAasRegAdministration(); + + AdministrativeInformation actualAdministrativeInformation = attributeMapper.mapAdministration(RegistryIntegrationTestHelper.getAas4jAdministration()); + + assertEquals(expectedAdministrativeInformation, actualAdministrativeInformation); + } + + @Test + public void mapAssetKind() { + AssetKind expectedAssetKind = RegistryIntegrationTestHelper.AASREG_ASSET_KIND; + + AssetKind actualAssetKind = attributeMapper.mapAssetKind(RegistryIntegrationTestHelper.AAS4J_ASSET_KIND); + + assertEquals(expectedAssetKind, actualAssetKind); + } + + private static ObjectMapper configureObjectMapper() { + List extensions = Arrays.asList(new Aas4JHTTPSerializationExtension()); + + return new BaSyxHTTPConfiguration().jackson2ObjectMapperBuilder(extensions).build(); + } + +} diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/resources/AasSimple_1.json b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/resources/AasSimple_1.json new file mode 100644 index 000000000..16e79e653 --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/resources/AasSimple_1.json @@ -0,0 +1,9 @@ +{ + "modelType": "AssetAdministrationShell", + "idShort": "ExampleMotor", + "id": "customIdentifier", + "assetInformation": { + "assetKind": "Instance", + "globalAssetId": "globalAssetId" + } +} diff --git a/basyx.aasrepository/basyx.aasrepository.component/pom.xml b/basyx.aasrepository/basyx.aasrepository.component/pom.xml index fa3ca06a9..eb586e7fb 100644 --- a/basyx.aasrepository/basyx.aasrepository.component/pom.xml +++ b/basyx.aasrepository/basyx.aasrepository.component/pom.xml @@ -1,4 +1,6 @@ - + 4.0.0 @@ -11,7 +13,8 @@ aas-repository - http://localhost:${docker.host.port}/shells + + http://localhost:${docker.host.port}/shells @@ -51,6 +54,16 @@ org.eclipse.digitaltwin.basyx basyx.aasrepository-feature-mqtt + + org.eclipse.digitaltwin.basyx + basyx.aasrepository-feature-registry-integration + + + org.eclipse.digitaltwin.basyx + basyx.aasrepository-feature-registry-integration + tests + test + org.eclipse.digitaltwin.basyx basyx.http @@ -107,7 +120,7 @@ - + @@ -127,12 +140,17 @@ - ${docker.host.port}:${docker.container.port} + + ${docker.host.port}:${docker.container.port} ${docker.container.waitForEndpoint} + + + integration + diff --git a/basyx.aasrepository/basyx.aasrepository.component/src/main/resources/application-integration.properties b/basyx.aasrepository/basyx.aasrepository.component/src/main/resources/application-integration.properties new file mode 100644 index 000000000..95043a9a8 --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository.component/src/main/resources/application-integration.properties @@ -0,0 +1,26 @@ +server.port=8081 +server.error.path=/error + +spring.application.name=AAS Repository +basyx.aasrepo.name=aas-repo + +basyx.backend = InMemory + +basyx.aasrepository.feature.registryintegration=http://host.docker.internal:8050/api/v3.0 +basyx.externalurl=http://localhost:8081 + +#basyx.backend = MongoDB +#spring.data.mongodb.host=127.0.0.1 +#spring.data.mongodb.port=27017 +#spring.data.mongodb.database=aas +#spring.data.mongodb.authentication-database=admin +#spring.data.mongodb.username=mongoAdmin +#spring.data.mongodb.password=mongoPassword + + +# basyx.aasrepository.feature.mqtt.enabled = true +# mqtt.clientId=TestClient +# mqtt.hostname = localhost +# mqtt.port = 1883 + +# basyx.cors.allowed-origins=http://localhost:3000, http://localhost:4000 diff --git a/basyx.aasrepository/basyx.aasrepository.component/src/main/resources/application.properties b/basyx.aasrepository/basyx.aasrepository.component/src/main/resources/application.properties index 4e939095a..1213f86ad 100644 --- a/basyx.aasrepository/basyx.aasrepository.component/src/main/resources/application.properties +++ b/basyx.aasrepository/basyx.aasrepository.component/src/main/resources/application.properties @@ -6,6 +6,9 @@ basyx.aasrepo.name=aas-repo basyx.backend = InMemory +# basyx.aasrepository.feature.registryintegration=http://localhost:8050/api/v3.0 +# basyx.externalurl=http://localhost:8081 + #basyx.backend = MongoDB #spring.data.mongodb.host=127.0.0.1 #spring.data.mongodb.port=27017 @@ -20,4 +23,4 @@ basyx.backend = InMemory # mqtt.hostname = localhost # mqtt.port = 1883 -# basyx.cors.allowed-origins=http://localhost:3000, http://localhost:4000 \ No newline at end of file +# basyx.cors.allowed-origins=http://localhost:3000, http://localhost:4000 diff --git a/basyx.aasrepository/basyx.aasrepository.component/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/component/AASRepositoryRegistryLinkIT.java b/basyx.aasrepository/basyx.aasrepository.component/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/component/AASRepositoryRegistryLinkIT.java new file mode 100644 index 000000000..f3e3a6162 --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository.component/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/component/AASRepositoryRegistryLinkIT.java @@ -0,0 +1,12 @@ +package org.eclipse.digitaltwin.basyx.aasrepository.component; + +import org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration.AasRepositoryRegistryTestLink; + +/** + * Integration test for the AAS Repository integration with AAS Registry + * + * @author danish + * + */ +public class AASRepositoryRegistryLinkIT extends AasRepositoryRegistryTestLink { +} diff --git a/basyx.aasrepository/pom.xml b/basyx.aasrepository/pom.xml index 57329c5c3..d628ddf59 100644 --- a/basyx.aasrepository/pom.xml +++ b/basyx.aasrepository/pom.xml @@ -18,6 +18,7 @@ basyx.aasrepository-backend-mongodb basyx.aasrepository-feature-aasxupload basyx.aasrepository-feature-mqtt + basyx.aasrepository-feature-registry-integration basyx.aasrepository-tck basyx.aasrepository.component diff --git a/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/RepositoryRegistryLinkException.java b/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/RepositoryRegistryLinkException.java new file mode 100644 index 000000000..83f6f3769 --- /dev/null +++ b/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/RepositoryRegistryLinkException.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.core.exceptions; + +/** + * Indicates failure of automatic link between Repository and Registry + * + * @author danish + * + */ +@SuppressWarnings("serial") +public class RepositoryRegistryLinkException extends RuntimeException { + public RepositoryRegistryLinkException() { + } + + public RepositoryRegistryLinkException(String id) { + super(getMessage(id)); + } + + private static String getMessage(String id) { + return "Unable to link the element with id '" + id + "' with the Registry."; + } + +} diff --git a/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/RepositoryRegistryUnlinkException.java b/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/RepositoryRegistryUnlinkException.java new file mode 100644 index 000000000..8532c3564 --- /dev/null +++ b/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/RepositoryRegistryUnlinkException.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.core.exceptions; + +/** + * Indicates failure of automatic unlink between Repository and Registry + * + * @author danish + * + */ +@SuppressWarnings("serial") +public class RepositoryRegistryUnlinkException extends RuntimeException { + public RepositoryRegistryUnlinkException() { + } + + public RepositoryRegistryUnlinkException(String id) { + super(getMessage(id)); + } + + private static String getMessage(String id) { + return "Unable to unlink the element with id '" + id + "' from the Registry."; + } + +} diff --git a/basyx.common/basyx.http/src/main/java/org/eclipse/digitaltwin/basyx/http/CustomTypeCloneFactory.java b/basyx.common/basyx.http/src/main/java/org/eclipse/digitaltwin/basyx/http/CustomTypeCloneFactory.java new file mode 100644 index 000000000..e3a8cdf94 --- /dev/null +++ b/basyx.common/basyx.http/src/main/java/org/eclipse/digitaltwin/basyx/http/CustomTypeCloneFactory.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.http; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Factory for creating clones for the defined Input and Output type + * + * @param the Input type + * @param the Output type + * + * @author danish + */ +public class CustomTypeCloneFactory { + + private Logger logger = LoggerFactory.getLogger(CustomTypeCloneFactory.class); + + private ObjectMapper mapper; + + private Class elementType; + + public CustomTypeCloneFactory(Class outputElementType, ObjectMapper mapper) { + super(); + this.elementType = outputElementType; + this.mapper = mapper; + } + + /** + * Creates clone for the provided input of {@link List} type + * + * @param input + * @return the cloned result + */ + public List create(List input) { + String serializedLangString = ""; + + try { + serializedLangString = mapper.writeValueAsString(input); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + + try { + return mapper.readValue(serializedLangString, mapper.getTypeFactory().constructCollectionType(List.class, elementType)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + + logger.error("Failure occurred while creating the clone"); + + return null; + } + + } + + /** + * Creates clone for the provided input + * + * @param input + * @return the cloned result + */ + public O create(I input) { + String serializedLangString = ""; + try { + serializedLangString = mapper.writeValueAsString(input); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + + try { + return mapper.readValue(serializedLangString, elementType); + } catch (JsonProcessingException e) { + e.printStackTrace(); + + logger.error("Failure occurred while creating the clone"); + + return null; + } + } + +} diff --git a/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/TestCustomTypeCloneFactory.java b/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/TestCustomTypeCloneFactory.java new file mode 100644 index 000000000..6e496a30c --- /dev/null +++ b/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/TestCustomTypeCloneFactory.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.http; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.digitaltwin.basyx.http.CustomTypeCloneFactory; +import org.eclipse.digitaltwin.basyx.http.testmodels.CloneFactoryFirstTestModel; +import org.eclipse.digitaltwin.basyx.http.testmodels.CloneFactorySecondTestModel; +import org.junit.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Unit tests for {@link CustomTypeCloneFactory} + * + * @author danish + */ +public class TestCustomTypeCloneFactory { + + @Test + public void createCloneOfListType() { + List expectedDescriptions = Arrays.asList(new CloneFactorySecondTestModel("Java", "Programming Language"), new CloneFactorySecondTestModel("Deutsch", "Language")); + + CustomTypeCloneFactory cloneFactory = new CustomTypeCloneFactory<>(CloneFactorySecondTestModel.class, new ObjectMapper()); + + List actualDescriptions = cloneFactory.create(Arrays.asList(new CloneFactoryFirstTestModel("Java", "Programming Language"), new CloneFactoryFirstTestModel("Deutsch", "Language"))); + + assertEquals(expectedDescriptions.size(), actualDescriptions.size()); + assertEquals(expectedDescriptions, actualDescriptions); + } + + @Test + public void createCloneOfNonListType() { + CloneFactoryFirstTestModel expectedAssetKind = new CloneFactoryFirstTestModel("Deutsch", "Language"); + + CustomTypeCloneFactory cloneFactory = new CustomTypeCloneFactory<>(CloneFactoryFirstTestModel.class, new ObjectMapper()); + + CloneFactoryFirstTestModel actualAssetKind = cloneFactory.create(new CloneFactorySecondTestModel("Deutsch", "Language")); + + assertEquals(expectedAssetKind, actualAssetKind); + } + +} diff --git a/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/testmodels/CloneFactoryFirstTestModel.java b/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/testmodels/CloneFactoryFirstTestModel.java new file mode 100644 index 000000000..afd7630af --- /dev/null +++ b/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/testmodels/CloneFactoryFirstTestModel.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.http.testmodels; + +import java.util.Objects; + +import org.eclipse.digitaltwin.basyx.http.CustomTypeCloneFactory; + +/** + * Simple model for testing {@link CustomTypeCloneFactory} + * + * @author danish + */ +public class CloneFactoryFirstTestModel { + + private String name; + private String type; + + public CloneFactoryFirstTestModel() {} + + public CloneFactoryFirstTestModel(String name, String type) { + super(); + this.name = name; + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public int hashCode() { + return Objects.hash(name, type); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CloneFactoryFirstTestModel other = (CloneFactoryFirstTestModel) obj; + return Objects.equals(name, other.name) && Objects.equals(type, other.type); + } + +} diff --git a/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/testmodels/CloneFactorySecondTestModel.java b/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/testmodels/CloneFactorySecondTestModel.java new file mode 100644 index 000000000..3878eb5e5 --- /dev/null +++ b/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/testmodels/CloneFactorySecondTestModel.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.http.testmodels; + +import java.util.Objects; + +/** + * Simple model for testing {@link CloneFactory} + * + * @author danish + */ +public class CloneFactorySecondTestModel { + + private String name; + private String type; + + public CloneFactorySecondTestModel() {} + + public CloneFactorySecondTestModel(String name, String type) { + super(); + this.name = name; + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public int hashCode() { + return Objects.hash(name, type); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CloneFactorySecondTestModel other = (CloneFactorySecondTestModel) obj; + return Objects.equals(name, other.name) && Objects.equals(type, other.type); + } + +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/Readme.md b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/Readme.md new file mode 100644 index 000000000..a45b2a3b5 --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/Readme.md @@ -0,0 +1,19 @@ +# Submodel Repository - Registry Integration +This feature automatically integrates the Descriptor with the Registry while creation of the Submodel at the Repository.
+It also automatically removes the Descriptor from the Registry when the Submodel is removed from the Repository. + +To enable this feature, the following two properties should be configured: + +``` +basyx.submodelrepository.feature.registryintegration = {Submodel-Registry-Base-Url} +basyx.externalurl = {Submodel-Repo-Base-Url} +``` + +This feature gets enable automatically when both of the above defined properties are configured, i.e., no external enabled/disabled property is required. + +An example valid configuration: + +``` +basyx.submodelrepository.feature.registryintegration = http://localhost:8060/api/v3.0 +basyx.externalurl = http://localhost:8081 +``` diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/pom.xml b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/pom.xml new file mode 100644 index 000000000..4052ba5ef --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/pom.xml @@ -0,0 +1,45 @@ + + 4.0.0 + + + org.eclipse.digitaltwin.basyx + basyx.submodelrepository + ${revision} + + + basyx.submodelrepository-feature-registry-integration + + + + org.eclipse.digitaltwin.basyx + basyx.submodelregistry-client-native + + + org.eclipse.digitaltwin.basyx + basyx.submodelrepository-core + + + org.eclipse.digitaltwin.basyx + basyx.http + test + tests + + + org.eclipse.digitaltwin.basyx + basyx.http + + + org.eclipse.digitaltwin.basyx + basyx.submodelrepository-http + tests + test + + + org.apache.httpcomponents.client5 + httpclient5 + test + + + diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepository.java new file mode 100644 index 000000000..a25b22acf --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepository.java @@ -0,0 +1,211 @@ +/******************************************************************************* + * Copyright (C) 2021 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration; + +import java.io.File; +import java.io.InputStream; +import java.util.List; + +import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable; +import org.eclipse.digitaltwin.aas4j.v3.model.Submodel; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; +import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException; +import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; +import org.eclipse.digitaltwin.basyx.core.exceptions.ElementNotAFileException; +import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException; +import org.eclipse.digitaltwin.basyx.core.exceptions.RepositoryRegistryLinkException; +import org.eclipse.digitaltwin.basyx.core.exceptions.RepositoryRegistryUnlinkException; +import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; +import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.ApiException; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.api.SubmodelRegistryApi; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.SubmodelDescriptor; +import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; +import org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration.mapper.AttributeMapper; +import org.eclipse.digitaltwin.basyx.submodelservice.value.SubmodelElementValue; +import org.eclipse.digitaltwin.basyx.submodelservice.value.SubmodelValueOnly; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Decorator for linking {@link SubmodelRepository} with SubmodelRegistry + * + * @author danish + * + */ +public class RegistryIntegrationSubmodelRepository implements SubmodelRepository { + private static Logger logger = LoggerFactory.getLogger(RegistryIntegrationSubmodelRepository.class); + + private SubmodelRepository decorated; + private SubmodelRepositoryRegistryLink submodelRepositoryRegistryLink; + private AttributeMapper attributeMapper; + + public RegistryIntegrationSubmodelRepository(SubmodelRepository decorated, SubmodelRepositoryRegistryLink submodelRepositoryRegistryLink, AttributeMapper attributeMapper) { + this.decorated = decorated; + this.submodelRepositoryRegistryLink = submodelRepositoryRegistryLink; + this.attributeMapper = attributeMapper; + } + + @Override + public CursorResult> getAllSubmodels(PaginationInfo paginationInfo) { + return decorated.getAllSubmodels(paginationInfo); + } + + @Override + public Submodel getSubmodel(String submodelId) throws ElementDoesNotExistException { + return decorated.getSubmodel(submodelId); + } + + @Override + public void updateSubmodel(String submodelId, Submodel submodel) throws ElementDoesNotExistException { + decorated.updateSubmodel(submodelId, submodel); + } + + @Override + public void createSubmodel(Submodel submodel) throws CollidingIdentifierException { + decorated.createSubmodel(submodel); + + integrateSubmodelWithRegistry(submodel, submodelRepositoryRegistryLink.getSubmodelRepositoryBaseURL()); + } + + @Override + public void deleteSubmodel(String submodelId) throws ElementDoesNotExistException { + deleteFromRegistry(submodelId); + + decorated.deleteSubmodel(submodelId); + } + + @Override + public CursorResult> getSubmodelElements(String submodelId, PaginationInfo paginationInfo) throws ElementDoesNotExistException { + return decorated.getSubmodelElements(submodelId, paginationInfo); + } + + @Override + public SubmodelElement getSubmodelElement(String submodelId, String submodelElementIdShort) throws ElementDoesNotExistException { + return decorated.getSubmodelElement(submodelId, submodelElementIdShort); + } + + @Override + public SubmodelElementValue getSubmodelElementValue(String submodelId, String submodelElementIdShort) throws ElementDoesNotExistException { + return decorated.getSubmodelElementValue(submodelId, submodelElementIdShort); + } + + @Override + public void setSubmodelElementValue(String submodelId, String idShortPath, SubmodelElementValue value) throws ElementDoesNotExistException { + decorated.setSubmodelElementValue(submodelId, idShortPath, value); + } + + @Override + public void createSubmodelElement(String submodelId, SubmodelElement submodelElement) { + decorated.createSubmodelElement(submodelId, submodelElement); + } + + @Override + public void createSubmodelElement(String submodelId, String idShortPath, SubmodelElement submodelElement) throws ElementDoesNotExistException { + decorated.createSubmodelElement(submodelId, submodelElement); + } + + @Override + public void deleteSubmodelElement(String submodelId, String idShortPath) throws ElementDoesNotExistException { + decorated.deleteSubmodelElement(submodelId, idShortPath); + } + + @Override + public OperationVariable[] invokeOperation(String submodelId, String idShortPath, OperationVariable[] input) throws ElementDoesNotExistException { + return decorated.invokeOperation(submodelId, idShortPath, input); + } + + @Override + public SubmodelValueOnly getSubmodelByIdValueOnly(String submodelId) throws ElementDoesNotExistException { + return decorated.getSubmodelByIdValueOnly(submodelId); + } + + @Override + public Submodel getSubmodelByIdMetadata(String submodelId) throws ElementDoesNotExistException { + return decorated.getSubmodelByIdMetadata(submodelId); + } + + @Override + public File getFileByPathSubmodel(String submodelId, String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException { + return decorated.getFileByPathSubmodel(submodelId, idShortPath); + } + + @Override + public void setFileValue(String submodelId, String idShortPath, String fileName, InputStream inputStream) throws ElementDoesNotExistException, ElementNotAFileException { + decorated.setFileValue(submodelId, idShortPath, fileName, inputStream); + } + + @Override + public void deleteFileValue(String submodelId, String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException { + decorated.deleteFileValue(submodelId, idShortPath); + } + + private void integrateSubmodelWithRegistry(Submodel submodel, String submodelRepositoryURL) { + SubmodelDescriptor descriptor = new SubmodelDescriptorFactory(submodel, submodelRepositoryURL, attributeMapper).create(); + + SubmodelRegistryApi registryApi = submodelRepositoryRegistryLink.getRegistryApi(); + + try { + registryApi.postSubmodelDescriptor(descriptor); + + logger.info("Submodel '{}' has been automatically linked with the Registry", submodel.getId()); + } catch (ApiException e) { + e.printStackTrace(); + + throw new RepositoryRegistryLinkException(submodel.getId()); + } + } + + private void deleteFromRegistry(String submodelId) { + SubmodelRegistryApi registryApi = submodelRepositoryRegistryLink.getRegistryApi(); + + if (!submodelExistsOnRegistry(submodelId, registryApi)) { + logger.error("Unable to un-link the Submodel descriptor '{}' from the Registry because it does not exist on the Registry.", submodelId); + + return; + } + + try { + registryApi.deleteSubmodelDescriptorById(submodelId); + + logger.info("Submodel '{}' has been automatically un-linked from the Registry.", submodelId); + } catch (ApiException e) { + e.printStackTrace(); + + throw new RepositoryRegistryUnlinkException(submodelId); + } + } + + private boolean submodelExistsOnRegistry(String submodelId, SubmodelRegistryApi registryApi) { + try { + registryApi.getSubmodelDescriptorById(submodelId); + + return true; + } catch (ApiException e) { + return false; + } + } + +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepositoryConfiguration.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepositoryConfiguration.java new file mode 100644 index 000000000..abb3341e2 --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepositoryConfiguration.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration; + +import org.eclipse.digitaltwin.basyx.submodelregistry.client.api.SubmodelRegistryApi; +import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; +import org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration.mapper.AttributeMapper; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Configuration for integrating {@link SubmodelRepository} with SubmodelRegistry + * + * @author danish + */ +@Configuration +@ConditionalOnExpression("!T(org.springframework.util.StringUtils).isEmpty('${basyx.submodelrepository.feature.registryintegration:}') && !T(org.springframework.util.StringUtils).isEmpty('${basyx.externalurl:}')") +public class RegistryIntegrationSubmodelRepositoryConfiguration { + + @Bean + @ConditionalOnMissingBean + public SubmodelRepositoryRegistryLink getSubmodelRepositoryRegistryLink(@Value("${basyx.submodelrepository.feature.registryintegration}") String registryBasePath, @Value("${basyx.externalurl}") String submodelRepositoryBaseURL) { + + return new SubmodelRepositoryRegistryLink(new SubmodelRegistryApi(registryBasePath), submodelRepositoryBaseURL); + } + + @Bean + @ConditionalOnMissingBean + public AttributeMapper getSubmodelAttributeMapper(ObjectMapper objectMapper) { + + return new AttributeMapper(objectMapper); + } + +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepositoryFactory.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepositoryFactory.java new file mode 100644 index 000000000..7a01eac01 --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepositoryFactory.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration; + +import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; +import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepositoryFactory; +import org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration.mapper.AttributeMapper; + +/** + * Factory for creating {@link RegistryIntegrationSubmodelRepository} + * + * @author danish + */ +public class RegistryIntegrationSubmodelRepositoryFactory implements SubmodelRepositoryFactory { + + private SubmodelRepositoryFactory decorated; + private SubmodelRepositoryRegistryLink submodelRepositoryRegistryLink; + private AttributeMapper attributeMapper; + + public RegistryIntegrationSubmodelRepositoryFactory(SubmodelRepositoryFactory decorated, SubmodelRepositoryRegistryLink submodelRepositoryRegistryLink, AttributeMapper attributeMapper) { + this.decorated = decorated; + this.submodelRepositoryRegistryLink = submodelRepositoryRegistryLink; + this.attributeMapper = attributeMapper; + } + + @Override + public SubmodelRepository create() { + return new RegistryIntegrationSubmodelRepository(decorated.create(), submodelRepositoryRegistryLink, attributeMapper); + } + +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepositoryFeature.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepositoryFeature.java new file mode 100644 index 000000000..bdf50882a --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepositoryFeature.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration; + +import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepositoryFactory; +import org.eclipse.digitaltwin.basyx.submodelrepository.feature.SubmodelRepositoryFeature; +import org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration.mapper.AttributeMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +/** + * Feature for integrating Registry with {@link AasRepository} + * + * @author danish + */ +@Component +@ConditionalOnExpression("!T(org.springframework.util.StringUtils).isEmpty('${basyx.submodelrepository.feature.registryintegration:}') && !T(org.springframework.util.StringUtils).isEmpty('${basyx.externalurl:}')") +public class RegistryIntegrationSubmodelRepositoryFeature implements SubmodelRepositoryFeature { + public static final String FEATURENAME = "basyx.submodelrepository.feature.registryintegration"; + + private SubmodelRepositoryRegistryLink submodelRepositoryRegistryLink; + + @Value("${" + FEATURENAME + ":}") + private String registryBaseURL; + + @Value("${basyx.externalurl:}") + private String submodelRepositoryExternalBaseURL; + + private AttributeMapper attributeMapper; + + @Autowired + public RegistryIntegrationSubmodelRepositoryFeature(SubmodelRepositoryRegistryLink submodelRepositoryRegistryLink, AttributeMapper attributeMapper) { + this.submodelRepositoryRegistryLink = submodelRepositoryRegistryLink; + this.attributeMapper = attributeMapper; + } + + @Override + public SubmodelRepositoryFactory decorate(SubmodelRepositoryFactory submodelRepositoryFactory) { + return new RegistryIntegrationSubmodelRepositoryFactory(submodelRepositoryFactory, submodelRepositoryRegistryLink, attributeMapper); + } + + @Override + public void initialize() { + } + + @Override + public void cleanUp() { + + } + + @Override + public String getName() { + return "SubmodelRepository Registry Integration"; + } + + @Override + public boolean isEnabled() { + return !registryBaseURL.isBlank() && !submodelRepositoryExternalBaseURL.isBlank(); + } +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelDescriptorFactory.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelDescriptorFactory.java new file mode 100644 index 000000000..572fb8e31 --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelDescriptorFactory.java @@ -0,0 +1,189 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; + +import org.eclipse.digitaltwin.aas4j.v3.model.AdministrativeInformation; +import org.eclipse.digitaltwin.aas4j.v3.model.Extension; +import org.eclipse.digitaltwin.aas4j.v3.model.LangStringNameType; +import org.eclipse.digitaltwin.aas4j.v3.model.LangStringTextType; +import org.eclipse.digitaltwin.aas4j.v3.model.Reference; +import org.eclipse.digitaltwin.aas4j.v3.model.Submodel; +import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Endpoint; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.ProtocolInformation; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.SubmodelDescriptor; +import org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration.mapper.AttributeMapper; + +/** + * Factory for creating the {@link SubmodelDescriptor} + * + * @author danish + */ +public class SubmodelDescriptorFactory { + + private static final String SUBMODEL_INTERFACE = "SUBMODEL-3.0"; + private static final String SUBMODEL_REPOSITORY_PATH = "/submodels"; + + private Submodel submodel; + private String submodelRepositoryURL; + + private AttributeMapper attributeMapper; + + public SubmodelDescriptorFactory(Submodel submodel, String submodelRepositoryBaseURL, AttributeMapper attributeMapper) { + super(); + this.submodel = submodel; + this.submodelRepositoryURL = createSubmodelRepositoryUrl(submodelRepositoryBaseURL); + this.attributeMapper = attributeMapper; + } + + /** + * Creates {@link SubmodelDescriptor} + * + * @return the created {@link SubmodelDescriptor} + */ + public SubmodelDescriptor create() { + + SubmodelDescriptor descriptor = new SubmodelDescriptor(); + + setId(submodel.getId(), descriptor); + + setIdShort(submodel.getIdShort(), descriptor); + + setEndpointItem(submodel.getId(), descriptor); + + setDescription(submodel.getDescription(), descriptor); + + setDisplayName(submodel.getDisplayName(), descriptor); + + setExtensions(submodel.getExtensions(), descriptor); + + setAdministration(submodel.getAdministration(), descriptor); + + setSemanticId(submodel.getSemanticId(), descriptor); + + setSupplementalSemanticId(submodel.getSupplementalSemanticIds(), descriptor); + + return descriptor; + } + + private void setDescription(List descriptions, SubmodelDescriptor descriptor) { + + if (descriptions == null || descriptions.isEmpty()) + return; + + descriptor.setDescription(attributeMapper.mapDescription(descriptions)); + } + + private void setDisplayName(List displayNames, SubmodelDescriptor descriptor) { + + if (displayNames == null || displayNames.isEmpty()) + return; + + descriptor.setDisplayName(attributeMapper.mapDisplayName(displayNames)); + } + + private void setExtensions(List extensions, SubmodelDescriptor descriptor) { + + if (extensions == null || extensions.isEmpty()) + return; + + descriptor.setExtensions(attributeMapper.mapExtensions(extensions)); + } + + private void setAdministration(AdministrativeInformation administration, SubmodelDescriptor descriptor) { + + if (administration == null) + return; + + descriptor.setAdministration(attributeMapper.mapAdministration(administration)); + } + + private void setSemanticId(Reference reference, SubmodelDescriptor descriptor) { + + if (reference == null) + return; + + descriptor.setSemanticId(attributeMapper.mapSemanticId(reference)); + } + + private void setSupplementalSemanticId(List supplementalSemanticIds, SubmodelDescriptor descriptor) { + + if (supplementalSemanticIds == null || supplementalSemanticIds.isEmpty()) + return; + + descriptor.setSupplementalSemanticId(attributeMapper.mapSupplementalSemanticId(supplementalSemanticIds)); + } + + private void setEndpointItem(String shellId, SubmodelDescriptor descriptor) { + + Endpoint endpoint = new Endpoint(); + endpoint.setInterface(SUBMODEL_INTERFACE); + ProtocolInformation protocolInformation = createProtocolInformation(shellId); + endpoint.setProtocolInformation(protocolInformation); + + descriptor.addEndpointsItem(endpoint); + } + + private ProtocolInformation createProtocolInformation(String shellId) { + String href = String.format("%s/%s", submodelRepositoryURL, Base64UrlEncodedIdentifier.encodeIdentifier(shellId)); + + ProtocolInformation protocolInformation = new ProtocolInformation(); + protocolInformation.endpointProtocol(getProtocol(href)); + protocolInformation.setHref(href); + + return protocolInformation; + } + + private void setIdShort(String idShort, SubmodelDescriptor descriptor) { + descriptor.setIdShort(idShort); + } + + private void setId(String shellId, SubmodelDescriptor descriptor) { + descriptor.setId(shellId); + } + + private String getProtocol(String endpoint) { + try { + return new URL(endpoint).getProtocol(); + } catch (MalformedURLException e) { + throw new RuntimeException(); + } + } + + private String createSubmodelRepositoryUrl(String submodelRepositoryBaseURL) { + + try { + return new URL(new URL(submodelRepositoryBaseURL), SUBMODEL_REPOSITORY_PATH).toString(); + } catch (MalformedURLException e) { + throw new RuntimeException("The Submodel Repository Base url is malformed.\n" + e.getMessage()); + } + } + +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLink.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLink.java new file mode 100644 index 000000000..56b1198ca --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLink.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration; + +import org.eclipse.digitaltwin.basyx.submodelregistry.client.api.SubmodelRegistryApi; +import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; + +/** + * Represents information for linking {@link SubmodelRepository} with SubmodelRegistry + * + * @author danish + */ +public class SubmodelRepositoryRegistryLink { + + private SubmodelRegistryApi registryApi; + private String submodelRepositoryBaseURL; + + public SubmodelRepositoryRegistryLink(SubmodelRegistryApi registryApi, String submodelRepositoryBaseURL) { + super(); + this.registryApi = registryApi; + this.submodelRepositoryBaseURL = submodelRepositoryBaseURL; + } + + public SubmodelRegistryApi getRegistryApi() { + return registryApi; + } + + public String getSubmodelRepositoryBaseURL() { + return submodelRepositoryBaseURL; + } + +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/mapper/AttributeMapper.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/mapper/AttributeMapper.java new file mode 100644 index 000000000..baad951d3 --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/mapper/AttributeMapper.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration.mapper; + +import java.util.List; + +import org.eclipse.digitaltwin.aas4j.v3.model.Submodel; +import org.eclipse.digitaltwin.basyx.http.CustomTypeCloneFactory; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.AdministrativeInformation; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Extension; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.LangStringNameType; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.LangStringTextType; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Maps the models defined in AAS4J to the SubmodelRegistry client models + * + * @author danish + */ +public class AttributeMapper { + + private Logger logger = LoggerFactory.getLogger(AttributeMapper.class); + + private ObjectMapper mapper; + + public AttributeMapper(ObjectMapper mapper) { + this.mapper = mapper; + } + + /** + * Maps {@link Submodel#getDescription()} from AAS4J to SubmodelRegistry client + * + * @param descriptions + * @return the mapped descriptions + */ + public List mapDescription(List descriptions) { + CustomTypeCloneFactory cloneFactory = new CustomTypeCloneFactory<>(LangStringTextType.class, mapper); + + List mappedDescriptions = cloneFactory.create(descriptions); + + if (mappedDescriptions == null) + logger.error("Descriptions could not be mapped due to a failure."); + + return mappedDescriptions; + } + + /** + * Maps {@link Submodel#getDisplayName()} from AAS4J to SubmodelRegistry client + * + * @param displayNames + * @return the mapped displayNames + */ + public List mapDisplayName(List displayNames) { + CustomTypeCloneFactory cloneFactory = new CustomTypeCloneFactory<>(LangStringNameType.class, mapper); + + List mappedDisplayNames = cloneFactory.create(displayNames); + + if (mappedDisplayNames == null) + logger.error("DisplayNames could not be mapped due to a failure."); + + return mappedDisplayNames; + } + + /** + * Maps {@link Submodel#getExtensions()} from AAS4J to SubmodelRegistry client + * + * @param extensions + * @return the mapped extensions + */ + public List mapExtensions(List extensions) { + CustomTypeCloneFactory cloneFactory = new CustomTypeCloneFactory<>(Extension.class, mapper); + + List mappedExtensions = cloneFactory.create(extensions); + + if (mappedExtensions == null) + logger.error("Extensions could not be mapped due to a failure."); + + return cloneFactory.create(extensions); + } + + /** + * Maps {@link Submodel#getAdministration()} from AAS4J to SubmodelRegistry + * client + * + * @param administrativeInformation + * @return the mapped administrativeInformation + */ + public AdministrativeInformation mapAdministration(org.eclipse.digitaltwin.aas4j.v3.model.AdministrativeInformation administrativeInformation) { + CustomTypeCloneFactory cloneFactory = new CustomTypeCloneFactory<>(AdministrativeInformation.class, mapper); + + AdministrativeInformation mappedAdministrativeInformation = cloneFactory.create(administrativeInformation); + + if (mappedAdministrativeInformation == null) + logger.error("AdministrativeInformation could not be mapped due to a failure."); + + return mappedAdministrativeInformation; + } + + /** + * Maps {@link Submodel#getSemanticID()} from AAS4J to SubmodelRegistry client + * + * @param semanticId + * @return the mapped semanticId + */ + public Reference mapSemanticId(org.eclipse.digitaltwin.aas4j.v3.model.Reference semanticId) { + CustomTypeCloneFactory cloneFactory = new CustomTypeCloneFactory<>(Reference.class, mapper); + + Reference mappedSemanticId = cloneFactory.create(semanticId); + + if (mappedSemanticId == null) + logger.error("SemanticId could not be mapped due to a failure."); + + return mappedSemanticId; + } + + /** + * Maps {@link Submodel#getSupplementalSemanticIds()} from AAS4J to + * SubmodelRegistry client + * + * @param supplementalSemanticIds + * @return the mapped supplementalSemanticIds + */ + public List mapSupplementalSemanticId(List supplementalSemanticIds) { + CustomTypeCloneFactory cloneFactory = new CustomTypeCloneFactory<>(Reference.class, mapper); + + List mappedSupplementalSemanticId = cloneFactory.create(supplementalSemanticIds); + + if (mappedSupplementalSemanticId == null) + logger.error("SupplementalSemanticId could not be mapped due to a failure."); + + return mappedSupplementalSemanticId; + } + +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationTestHelper.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationTestHelper.java new file mode 100644 index 000000000..fb0de5ec0 --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationTestHelper.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.digitaltwin.aas4j.v3.model.AdministrativeInformation; +import org.eclipse.digitaltwin.aas4j.v3.model.EmbeddedDataSpecification; +import org.eclipse.digitaltwin.aas4j.v3.model.Extension; +import org.eclipse.digitaltwin.aas4j.v3.model.KeyTypes; +import org.eclipse.digitaltwin.aas4j.v3.model.LangStringNameType; +import org.eclipse.digitaltwin.aas4j.v3.model.LangStringTextType; +import org.eclipse.digitaltwin.aas4j.v3.model.Reference; +import org.eclipse.digitaltwin.aas4j.v3.model.ReferenceTypes; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultAdministrativeInformation; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultEmbeddedDataSpecification; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultExtension; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultKey; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultLangStringNameType; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultLangStringTextType; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultReference; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.DataTypeDefXsd; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Key; + +/** + * A helper class for testing RegistryIntegration feature + * + * @author danish + */ +public class RegistryIntegrationTestHelper { + + // LangStringTextType AAS4J + private static final LangStringTextType AAS4J_LANG_STRING_TEXT_TYPE_1 = new DefaultLangStringTextType.Builder().language("de").text("Ein Beispiel").build(); + private static final LangStringTextType AAS4J_LANG_STRING_TEXT_TYPE_2 = new DefaultLangStringTextType.Builder().language("en").text("An Example").build(); + + // LangStringTextType SMRegistry + private static final org.eclipse.digitaltwin.basyx.submodelregistry.client.model.LangStringTextType SUBMODEL_REG_LANG_STRING_TEXT_TYPE_1 = new org.eclipse.digitaltwin.basyx.submodelregistry.client.model.LangStringTextType().language("de") + .text("Ein Beispiel"); + private static final org.eclipse.digitaltwin.basyx.submodelregistry.client.model.LangStringTextType SUBMODEL_REG_LANG_STRING_TEXT_TYPE_2 = new org.eclipse.digitaltwin.basyx.submodelregistry.client.model.LangStringTextType().language("en") + .text("An Example"); + + // LangStringNameType AAS4J + private static final LangStringNameType AAS4J_LANG_STRING_NAME_TYPE_1 = new DefaultLangStringNameType.Builder().language("en").text("Name type string").build(); + private static final LangStringNameType AAS4J_LANG_STRING_NAME_TYPE_2 = new DefaultLangStringNameType.Builder().language("de").text("Namenstypzeichenfolge").build(); + + // LangStringNameType SMRegistry + private static final org.eclipse.digitaltwin.basyx.submodelregistry.client.model.LangStringNameType SUBMODEL_REG_LANG_STRING_NAME_TYPE_1 = new org.eclipse.digitaltwin.basyx.submodelregistry.client.model.LangStringNameType().language("en") + .text("Name type string"); + private static final org.eclipse.digitaltwin.basyx.submodelregistry.client.model.LangStringNameType SUBMODEL_REG_LANG_STRING_NAME_TYPE_2 = new org.eclipse.digitaltwin.basyx.submodelregistry.client.model.LangStringNameType().language("de") + .text("Namenstypzeichenfolge"); + + // Administration AAS4J + private static final Reference AAS4J_DATASPECIFICATION = new DefaultReference.Builder().keys(new DefaultKey.Builder().type(KeyTypes.BLOB).value("BlobValue").build()).type(ReferenceTypes.EXTERNAL_REFERENCE).build(); + private static final EmbeddedDataSpecification AAS4JEMB_EMBEDDED_DATA_SPECIFICATION = new DefaultEmbeddedDataSpecification.Builder().dataSpecification(AAS4J_DATASPECIFICATION).build(); + + // Administration SMRegistry + private static final org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Reference SUBMODEL_REG_DATASPECIFICATION = new org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Reference() + .keys(Arrays.asList(new Key().type(org.eclipse.digitaltwin.basyx.submodelregistry.client.model.KeyTypes.BLOB).value("BlobValue"))).type(org.eclipse.digitaltwin.basyx.submodelregistry.client.model.ReferenceTypes.EXTERNALREFERENCE); + private static final org.eclipse.digitaltwin.basyx.submodelregistry.client.model.EmbeddedDataSpecification SUBMODEL_REG_EMBEDDED_DATA_SPECIFICATION = new org.eclipse.digitaltwin.basyx.submodelregistry.client.model.EmbeddedDataSpecification() + .dataSpecification(SUBMODEL_REG_DATASPECIFICATION); + + private static final String VERSION = "1.0.0"; + private static final String REVISION = "3"; + private static final String TEMPLATEID = "ID2.0"; + + // Extension AAS4J + private static final Extension AAS4J_EXTENSION = new DefaultExtension.Builder().semanticId(AAS4J_DATASPECIFICATION).name("extension").valueType(org.eclipse.digitaltwin.aas4j.v3.model.DataTypeDefXsd.STRING).value("extensionValue").build(); + + // Extension SMRegistry + private static final org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Extension SUBMODEL_REG_EXTENSION = new org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Extension().semanticId(SUBMODEL_REG_DATASPECIFICATION).name("extension") + .valueType(DataTypeDefXsd.STRING).value("extensionValue"); + + public static List getAas4jLangStringTextTypes() { + return Arrays.asList(AAS4J_LANG_STRING_TEXT_TYPE_1, AAS4J_LANG_STRING_TEXT_TYPE_2); + } + + public static List getSubmodelRegLangStringTextTypes() { + return Arrays.asList(SUBMODEL_REG_LANG_STRING_TEXT_TYPE_1, SUBMODEL_REG_LANG_STRING_TEXT_TYPE_2); + } + + public static AdministrativeInformation getAas4jAdministration() { + return new DefaultAdministrativeInformation.Builder().embeddedDataSpecifications(AAS4JEMB_EMBEDDED_DATA_SPECIFICATION).version(VERSION).revision(REVISION).templateId(TEMPLATEID).build(); + } + + public static org.eclipse.digitaltwin.basyx.submodelregistry.client.model.AdministrativeInformation getSubmodelRegAdministration() { + return new org.eclipse.digitaltwin.basyx.submodelregistry.client.model.AdministrativeInformation().embeddedDataSpecifications(Arrays.asList(SUBMODEL_REG_EMBEDDED_DATA_SPECIFICATION)).version(VERSION).revision(REVISION) + .templateId(TEMPLATEID); + } + + public static List getAas4jLangStringNameTypes() { + return Arrays.asList(AAS4J_LANG_STRING_NAME_TYPE_1, AAS4J_LANG_STRING_NAME_TYPE_2); + } + + public static List getSubmodelRegLangStringNameTypes() { + return Arrays.asList(SUBMODEL_REG_LANG_STRING_NAME_TYPE_1, SUBMODEL_REG_LANG_STRING_NAME_TYPE_2); + } + + public static List getAas4jExtensions() { + return Arrays.asList(AAS4J_EXTENSION); + } + + public static List getSubmodelRegExtensions() { + return Arrays.asList(SUBMODEL_REG_EXTENSION); + } + + public static Reference getAas4jSemanticId() { + return AAS4J_DATASPECIFICATION; + } + + public static org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Reference getSubmodelRegSemanticId() { + return SUBMODEL_REG_DATASPECIFICATION; + } + + public static List getAas4jSupplementalSemanticIds() { + return Arrays.asList(AAS4J_DATASPECIFICATION, AAS4J_DATASPECIFICATION); + } + + public static List getSubmodelRegSupplementalSemanticIds() { + return Arrays.asList(SUBMODEL_REG_DATASPECIFICATION, SUBMODEL_REG_DATASPECIFICATION); + } + +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryTestLink.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryTestLink.java new file mode 100644 index 000000000..eb8bfe2ae --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryTestLink.java @@ -0,0 +1,191 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Arrays; +import java.util.List; + +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; +import org.eclipse.digitaltwin.basyx.http.serialization.BaSyxHttpTestUtils; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.ApiException; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.api.SubmodelRegistryApi; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Endpoint; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.GetSubmodelDescriptorsResult; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Key; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.KeyTypes; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.ProtocolInformation; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Reference; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.ReferenceTypes; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.SubmodelDescriptor; +import org.junit.Test; +import org.springframework.http.HttpStatus; + +/** + * Integration test for {@link RegistryIntegrationSubmodelRepository} feature + * + * @author danish + */ +public class SubmodelRepositoryRegistryTestLink { + + private static final String SUBMODEL_REPOSITORY_PATH = "/submodels"; + + private static final String SUMMY_SUBMODEL_IDSHORT = "TechnicalData"; + private static final String DUMMY_SUBMODEL_ID = "7A7104BDAB57E184"; + + public static String submodelRepoBaseUrl = "http://localhost:8081"; + public static String submodelRegistryUrl = "http://localhost:8060/api/v3.0"; + + private static final SubmodelDescriptor DUMMY_DESCRIPTOR = createExpectedDescriptor(); + + @Test + public void createSubmodel() throws FileNotFoundException, IOException, ApiException { + String submodelJsonContent = getSubmodelJSONString(); + + CloseableHttpResponse creationResponse = createSubmodelOnRepo(submodelJsonContent); + assertEquals(HttpStatus.CREATED.value(), creationResponse.getCode()); + + SubmodelDescriptor actualDescriptor = retrieveDescriptorFromRegistry(); + + assertEquals(DUMMY_DESCRIPTOR, actualDescriptor); + + resetRepository(); + } + + @Test + public void deleteSubmodel() throws FileNotFoundException, IOException, ApiException { + String submodelJsonContent = getSubmodelJSONString(); + + CloseableHttpResponse creationResponse = createSubmodelOnRepo(submodelJsonContent); + assertEquals(HttpStatus.CREATED.value(), creationResponse.getCode()); + + CloseableHttpResponse deleteResponse = deleteSubmodelFromRepo(DUMMY_SUBMODEL_ID); + assertEquals(HttpStatus.NO_CONTENT.value(), deleteResponse.getCode()); + + assertDescriptionDeletionAtRegistry(); + } + + private SubmodelDescriptor retrieveDescriptorFromRegistry() throws ApiException { + SubmodelRegistryApi api = new SubmodelRegistryApi(submodelRegistryUrl); + + return api.getSubmodelDescriptorById(DUMMY_SUBMODEL_ID); + } + + private void resetRepository() throws IOException { + CloseableHttpResponse deleteResponse = deleteSubmodelFromRepo(DUMMY_SUBMODEL_ID); + + assertEquals(HttpStatus.NO_CONTENT.value(), deleteResponse.getCode()); + } + + private CloseableHttpResponse deleteSubmodelFromRepo(String shellId) throws IOException { + return BaSyxHttpTestUtils.executeDeleteOnURL(getSpecificSubmodelAccessURL(shellId)); + } + + private void assertDescriptionDeletionAtRegistry() throws ApiException { + SubmodelRegistryApi api = new SubmodelRegistryApi(submodelRegistryUrl); + + GetSubmodelDescriptorsResult result = api.getAllSubmodelDescriptors(null, null); + + List actualDescriptors = result.getResult(); + + assertTrue(actualDescriptors.isEmpty()); + } + + private String getSubmodelJSONString() throws FileNotFoundException, IOException { + return BaSyxHttpTestUtils.readJSONStringFromClasspath("SingleSubmodel.json"); + } + + private CloseableHttpResponse createSubmodelOnRepo(String aasJsonContent) throws IOException { + return BaSyxHttpTestUtils.executePostOnURL(createSubmodelRepositoryUrl(submodelRepoBaseUrl), aasJsonContent); + } + + private String getSpecificSubmodelAccessURL(String aasId) { + return createSubmodelRepositoryUrl(submodelRepoBaseUrl) + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(aasId); + } + + private static SubmodelDescriptor createExpectedDescriptor() { + + SubmodelDescriptor descriptor = new SubmodelDescriptor(); + + descriptor.setId(DUMMY_SUBMODEL_ID); + descriptor.setIdShort(SUMMY_SUBMODEL_IDSHORT); + descriptor.setSemanticId(createSemanticId()); + descriptor.addEndpointsItem(createEndpointItem()); + + return descriptor; + } + + private static Reference createSemanticId() { + return new Reference().keys(Arrays.asList(new Key().type(KeyTypes.GLOBALREFERENCE).value("0173-1#01-AFZ615#016"))).type(ReferenceTypes.EXTERNALREFERENCE); + } + + private static Endpoint createEndpointItem() { + Endpoint endpoint = new Endpoint(); + endpoint.setInterface("SUBMODEL-3.0"); + endpoint.setProtocolInformation(createProtocolInformation()); + + return endpoint; + } + + private static ProtocolInformation createProtocolInformation() { + String href = createHref(); + + ProtocolInformation protocolInformation = new ProtocolInformation(); + protocolInformation.setHref(href); + protocolInformation.endpointProtocol(getProtocol(href)); + + return protocolInformation; + } + + private static String createHref() { + return String.format("%s/%s", createSubmodelRepositoryUrl(submodelRepoBaseUrl), Base64UrlEncodedIdentifier.encodeIdentifier(DUMMY_SUBMODEL_ID)); + } + + private static String getProtocol(String endpoint) { + try { + return new URL(endpoint).getProtocol(); + } catch (MalformedURLException e) { + throw new RuntimeException(); + } + } + + private static String createSubmodelRepositoryUrl(String smRepositoryBaseURL) { + + try { + return new URL(new URL(smRepositoryBaseURL), SUBMODEL_REPOSITORY_PATH).toString(); + } catch (MalformedURLException e) { + throw new RuntimeException("The Submodel Repository Base url is malformed.\n " + e.getMessage()); + } + } + +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryTestLinkApplication.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryTestLinkApplication.java new file mode 100644 index 000000000..6b1cd2687 --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryTestLinkApplication.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration; + +import org.junit.internal.TextListener; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; + +/** + * Application for testing the {@link RegistryIntegrationSubmodelRepository} + * feature. The first argument is the Submodel Repository Base URL, the second + * argument is the Submodel Registry URL. + * + * @author schnicke, danish + * + */ +public class SubmodelRepositoryRegistryTestLinkApplication { + + public static void main(String[] args) throws Exception { + String submodelRepoBaseUrl = getSubmodelRepositoryBaseUrl(args); + String submodelRegUrl = getSubmodelRegistryUrl(args); + + Result result = runTests(submodelRepoBaseUrl, submodelRegUrl); + + printResults(result); + } + + private static void printResults(Result result) { + System.out.println("Finished. Result: Failures: " + result.getFailureCount() + ". Ignored: " + result.getIgnoreCount() + ". Tests run: " + result.getRunCount() + ". Time: " + result.getRunTime() + "ms."); + } + + private static Result runTests(String submodelRepoBaseUrl, String submodelRegUrl) { + SubmodelRepositoryRegistryTestLink.submodelRepoBaseUrl = submodelRepoBaseUrl; + SubmodelRepositoryRegistryTestLink.submodelRegistryUrl = submodelRegUrl; + + JUnitCore junit = new JUnitCore(); + junit.addListener(new TextListener(System.out)); + + return junit.run(SubmodelRepositoryRegistryTestLink.class); + } + + private static String getSubmodelRepositoryBaseUrl(String[] args) { + return args[0]; + } + + private static String getSubmodelRegistryUrl(String[] args) { + return args[1]; + } + +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/TestAttributeMapper.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/TestAttributeMapper.java new file mode 100644 index 000000000..70c6c24cc --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/TestAttributeMapper.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.digitaltwin.basyx.http.Aas4JHTTPSerializationExtension; +import org.eclipse.digitaltwin.basyx.http.BaSyxHTTPConfiguration; +import org.eclipse.digitaltwin.basyx.http.SerializationExtension; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.AdministrativeInformation; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Extension; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.LangStringNameType; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.LangStringTextType; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Reference; +import org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration.mapper.AttributeMapper; +import org.junit.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Unit tests for {@link AttributeMapper} + * + * @author danish + */ +public class TestAttributeMapper { + + private static AttributeMapper attributeMapper = new AttributeMapper(configureObjectMapper()); + + @Test + public void mapDescriptions() { + List expectedDescriptions = RegistryIntegrationTestHelper.getSubmodelRegLangStringTextTypes(); + + List actualDescriptions = attributeMapper.mapDescription(RegistryIntegrationTestHelper.getAas4jLangStringTextTypes()); + + assertEquals(expectedDescriptions.size(), actualDescriptions.size()); + assertEquals(expectedDescriptions, actualDescriptions); + } + + @Test + public void mapDisplayNames() { + List expectedDisplayNames = RegistryIntegrationTestHelper.getSubmodelRegLangStringNameTypes(); + + List actualDisplayNames = attributeMapper.mapDisplayName(RegistryIntegrationTestHelper.getAas4jLangStringNameTypes()); + + assertEquals(expectedDisplayNames.size(), actualDisplayNames.size()); + assertEquals(expectedDisplayNames, actualDisplayNames); + } + + @Test + public void mapExtensions() { + List expectedExtensions = RegistryIntegrationTestHelper.getSubmodelRegExtensions(); + + List actualExtensions = attributeMapper.mapExtensions(RegistryIntegrationTestHelper.getAas4jExtensions()); + + assertEquals(expectedExtensions.size(), actualExtensions.size()); + assertEquals(expectedExtensions, actualExtensions); + } + + @Test + public void mapAdministration() { + AdministrativeInformation expectedAdministrativeInformation = RegistryIntegrationTestHelper.getSubmodelRegAdministration(); + + AdministrativeInformation actualAdministrativeInformation = attributeMapper.mapAdministration(RegistryIntegrationTestHelper.getAas4jAdministration()); + + assertEquals(expectedAdministrativeInformation, actualAdministrativeInformation); + } + + @Test + public void mapSemanticId() { + Reference expectedSemanticId = RegistryIntegrationTestHelper.getSubmodelRegSemanticId(); + + Reference actualSemanticId = attributeMapper.mapSemanticId(RegistryIntegrationTestHelper.getAas4jSemanticId()); + + assertEquals(expectedSemanticId, actualSemanticId); + } + + @Test + public void mapSupplementalSemanticId() { + List expectedSupplementalSemanticId = RegistryIntegrationTestHelper.getSubmodelRegSupplementalSemanticIds(); + + List actualSupplementalSemanticId = attributeMapper.mapSupplementalSemanticId(RegistryIntegrationTestHelper.getAas4jSupplementalSemanticIds()); + + assertEquals(expectedSupplementalSemanticId.size(), actualSupplementalSemanticId.size()); + assertEquals(expectedSupplementalSemanticId, actualSupplementalSemanticId); + } + + private static ObjectMapper configureObjectMapper() { + List extensions = Arrays.asList(new Aas4JHTTPSerializationExtension()); + + return new BaSyxHTTPConfiguration().jackson2ObjectMapperBuilder(extensions).build(); + } + +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/resources/SingleSubmodel.json b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/resources/SingleSubmodel.json new file mode 100644 index 000000000..3391b2db6 --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/resources/SingleSubmodel.json @@ -0,0 +1,405 @@ +{ + "modelType": "Submodel", + "id": "7A7104BDAB57E184", + "idShort": "TechnicalData", + "kind": "Instance", + "submodelElements": [ + { + "modelType": "Property", + "value": "5000", + "valueType": "xs:integer", + "category": "PARAMETER", + "idShort": "MaxRotationSpeed", + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + }, + { + "modelType": "Range", + "max": "300", + "min": "200", + "valueType": "xs:integer", + "category": "PARAMETER", + "idShort": "RotationSpeedRange", + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + }, + { + "modelType": "MultiLanguageProperty", + "category": "PARAM", + "idShort": "MultiLanguage", + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + }, + "value": [ + { + "language": "en", + "text": "Hello" + }, + { + "language": "de", + "text": "Hallo" + } + ] + }, + { + "modelType": "File", + "contentType": "application/json", + "value": "testFile.json", + "category": "PARAMETER", + "idShort": "FileData", + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + }, + { + "modelType": "Entity", + "entityType": "CoManagedEntity", + "specificAssetIds": [ + { + "name": "specificAssetIdName", + "value": "specificValue" + } + ], + "statements": [ + { + "modelType": "Property", + "value": "5000", + "valueType": "xs:integer", + "category": "PARAMETER", + "idShort": "MaxRotationSpeed", + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + }, + { + "modelType": "Range", + "max": "300", + "min": "200", + "valueType": "xs:integer", + "category": "PARAMETER", + "idShort": "RotationSpeedRange", + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + } + ], + "category": "Entity", + "idShort": "EntityData", + "globalAssetId": "globalAssetID", + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + }, + { + "modelType": "ReferenceElement", + "category": "PARAMETER", + "idShort": "ReferenceElement", + "value": { + "keys": [ + { + "type": "DataElement", + "value": "DataElement" + } + ], + "type": "ModelReference" + }, + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + }, + { + "modelType": "RelationshipElement", + "category": "PARAMETER", + "idShort": "RelationshipElement", + "first": { + "keys": [ + { + "type": "DataElement", + "value": "DataElement" + } + ], + "type": "ModelReference" + }, + "second": { + "keys": [ + { + "type": "BasicEventElement", + "value": "BasicEventElement" + } + ], + "type": "ExternalReference" + }, + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + }, + { + "modelType": "AnnotatedRelationshipElement", + "category": "PARAMETER", + "idShort": "AnnotatedRelationshipElement", + "first": { + "keys": [ + { + "type": "DataElement", + "value": "DataElement" + } + ], + "type": "ModelReference" + }, + "second": { + "keys": [ + { + "type": "BasicEventElement", + "value": "BasicEventElement" + } + ], + "type": "ExternalReference" + }, + "annotations": [ + { + "modelType": "Property", + "value": "5000", + "valueType": "xs:integer", + "category": "PARAMETER", + "idShort": "MaxRotationSpeed", + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + }, + { + "modelType": "Range", + "max": "300", + "min": "200", + "valueType": "xs:integer", + "category": "PARAMETER", + "idShort": "RotationSpeedRange", + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + } + ], + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + }, + { + "modelType": "Blob", + "contentType": "application/xml", + "value": "VGVzdCBjb250ZW50IG9mIFhNTCBmaWxl", + "category": "PARAMETER", + "idShort": "BlobData", + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + }, + { + "modelType": "SubmodelElementCollection", + "category": "PARAMETER", + "idShort": "SubmodelElementCollection", + "value": [ + { + "modelType": "File", + "contentType": "application/json", + "value": "testFile.json", + "category": "PARAMETER", + "idShort": "FileData", + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + }, + { + "modelType": "Property", + "value": "5000", + "valueType": "xs:integer", + "category": "PARAMETER", + "idShort": "MaxRotationSpeed", + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + } + ], + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + }, + { + "modelType": "SubmodelElementList", + "category": "PARAMETER", + "idShort": "SubmodelElementList", + "orderRelevant": true, + "value": [ + { + "modelType": "Range", + "max": "300", + "min": "200", + "valueType": "xs:integer", + "category": "PARAMETER", + "idShort": "RotationSpeedRange", + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + }, + { + "modelType": "Property", + "value": "5000", + "valueType": "xs:integer", + "category": "PARAMETER", + "idShort": "MaxRotationSpeed", + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + } + ], + "semanticId": { + "keys": [ + { + "type": "ConceptDescription", + "value": "0173-1#02-BAA120#008" + } + ], + "type": "ExternalReference" + } + }, + { + "modelType": "Operation", + "idShort": "square", + "inputVariables": [ + { + "value": { + "modelType": "Property", + "valueType": "xs:int", + "idShort": "input" + } + } + ], + "outputVariables": [ + { + "value": { + "modelType": "Property", + "valueType": "xs:int", + "idShort": "result" + } + } + ] + } + ], + "semanticId": { + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#01-AFZ615#016" + } + ], + "type": "ExternalReference" + } +} \ No newline at end of file diff --git a/basyx.submodelrepository/basyx.submodelrepository.component/pom.xml b/basyx.submodelrepository/basyx.submodelrepository.component/pom.xml index d8591fdc7..da656eaa7 100644 --- a/basyx.submodelrepository/basyx.submodelrepository.component/pom.xml +++ b/basyx.submodelrepository/basyx.submodelrepository.component/pom.xml @@ -1,4 +1,6 @@ - + 4.0.0 @@ -8,10 +10,11 @@ basyx.submodelrepository.component - + submodel-repository - http://localhost:${docker.host.port}/submodels + + http://localhost:${docker.host.port}/submodels @@ -35,6 +38,16 @@ org.eclipse.digitaltwin.basyx basyx.submodelrepository-feature-mqtt
+ + org.eclipse.digitaltwin.basyx + basyx.submodelrepository-feature-registry-integration + + + org.eclipse.digitaltwin.basyx + basyx.submodelrepository-feature-registry-integration + tests + test + org.eclipse.digitaltwin.basyx basyx.submodelrepository-http @@ -62,7 +75,7 @@ org.springframework.boot spring-boot-starter-actuator - + org.eclipse.digitaltwin.basyx basyx.submodelservice-core @@ -113,7 +126,7 @@ - + @@ -133,12 +146,17 @@ - ${docker.host.port}:${docker.container.port} + + ${docker.host.port}:${docker.container.port} ${docker.container.waitForEndpoint} + + + integration + @@ -171,7 +189,7 @@ - + docker diff --git a/basyx.submodelrepository/basyx.submodelrepository.component/src/main/resources/application-integration.properties b/basyx.submodelrepository/basyx.submodelrepository.component/src/main/resources/application-integration.properties new file mode 100644 index 000000000..5ae73e3f3 --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository.component/src/main/resources/application-integration.properties @@ -0,0 +1,26 @@ +server.port=8081 +server.error.path=/error + +spring.application.name=AAS Repository +basyx.smrepo.name = sm-repo + +basyx.backend = InMemory + +basyx.submodelrepository.feature.registryintegration=http://host.docker.internal:8060/api/v3.0 +basyx.externalurl=http://localhost:8081 + +#basyx.backend = MongoDB +#spring.data.mongodb.host=mongo +# or spring.data.mongodb.host=127.0.0.1 +#spring.data.mongodb.port=27017 +#spring.data.mongodb.database=submodels +#spring.data.mongodb.authentication-database=admin +#spring.data.mongodb.username=mongoAdmin +#spring.data.mongodb.password=mongoPassword + +# basyx.submodelrepository.feature.mqtt.enabled = true +# mqtt.clientId=TestClient +# mqtt.hostname = localhost +# mqtt.port = 1883 + +# basyx.cors.allowed-origins=http://localhost:3000, http://localhost:4000 diff --git a/basyx.submodelrepository/basyx.submodelrepository.component/src/main/resources/application.properties b/basyx.submodelrepository/basyx.submodelrepository.component/src/main/resources/application.properties index cbbabfbc1..1656e4e7c 100644 --- a/basyx.submodelrepository/basyx.submodelrepository.component/src/main/resources/application.properties +++ b/basyx.submodelrepository/basyx.submodelrepository.component/src/main/resources/application.properties @@ -4,6 +4,9 @@ spring.application.name=Submodel Repository basyx.smrepo.name = sm-repo basyx.backend = InMemory +# basyx.submodelrepository.feature.registryintegration=http://localhost:8060/api/v3.0 +# basyx.externalurl=http://localhost:8081 + #basyx.backend = MongoDB #spring.data.mongodb.host=mongo # or spring.data.mongodb.host=127.0.0.1 diff --git a/basyx.submodelrepository/basyx.submodelrepository.component/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/component/SubmodelRepositoryRegistryLinkIT.java b/basyx.submodelrepository/basyx.submodelrepository.component/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/component/SubmodelRepositoryRegistryLinkIT.java new file mode 100644 index 000000000..a0cfaea88 --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository.component/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/component/SubmodelRepositoryRegistryLinkIT.java @@ -0,0 +1,12 @@ +package org.eclipse.digitaltwin.basyx.submodelrepository.component; + +import org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration.SubmodelRepositoryRegistryTestLink; + +/** + * Integration test for the Submodel Repository integration with Submodel Registry + * + * @author danish + * + */ +public class SubmodelRepositoryRegistryLinkIT extends SubmodelRepositoryRegistryTestLink { +} diff --git a/basyx.submodelrepository/pom.xml b/basyx.submodelrepository/pom.xml index f1d918803..c38c3d1fb 100644 --- a/basyx.submodelrepository/pom.xml +++ b/basyx.submodelrepository/pom.xml @@ -16,6 +16,7 @@ basyx.submodelrepository-http basyx.submodelrepository-backend-inmemory basyx.submodelrepository-feature-mqtt + basyx.submodelrepository-feature-registry-integration basyx.submodelrepository-tck basyx.submodelrepository.component basyx.submodelrepository-backend-mongodb diff --git a/docker-compose.yml b/docker-compose.yml index cfb37a409..9408a3afb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -73,6 +73,24 @@ services: condition: service_healthy networks: - basyx-java-server-sdk + + aas-registry-log-mem: + image: eclipsebasyx/aas-registry-log-mem:2.0.0-SNAPSHOT + container_name: aas-registry-log-mem + ports: + - "8050:8080" + restart: always + networks: + - basyx-java-server-sdk + + sm-registry-log-mem: + image: eclipsebasyx/submodel-registry-log-mem:2.0.0-SNAPSHOT + container_name: sm-registry-log-mem + ports: + - "8060:8080" + restart: always + networks: + - basyx-java-server-sdk networks: basyx-java-server-sdk: diff --git a/pom.xml b/pom.xml index 8d725b726..184eceb72 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ basyx.aasregistry basyx.aasenvironment basyx.conceptdescriptionrepository - basyx.aasxfileserver + basyx.aasxfileserver basyx.aasdiscoveryservice BaSyx Parent @@ -288,14 +288,14 @@ ${revision} - org.eclipse.digitaltwin.basyx - basyx.submodelservice.component - ${revision} + org.eclipse.digitaltwin.basyx + basyx.submodelservice.component + ${revision} - org.eclipse.digitaltwin.basyx - basyx.submodelservice-http - ${revision} + org.eclipse.digitaltwin.basyx + basyx.submodelservice-http + ${revision} @@ -323,6 +323,11 @@ basyx.submodelrepository-feature-mqtt ${revision} + + org.eclipse.digitaltwin.basyx + basyx.submodelrepository-feature-registry-integration + ${revision} + org.eclipse.digitaltwin.basyx basyx.submodelrepository.component @@ -390,6 +395,11 @@ basyx.aasrepository-feature-mqtt ${revision} + + org.eclipse.digitaltwin.basyx + basyx.aasrepository-feature-registry-integration + ${revision} + org.eclipse.digitaltwin.basyx basyx.aasrepository.component @@ -441,7 +451,7 @@ org.eclipse.digitaltwin.basyx basyx.conceptdescriptionrepository.component ${revision} - + org.eclipse.digitaltwin.basyx @@ -478,7 +488,7 @@ org.eclipse.digitaltwin.basyx basyx.aasxfileserver-core ${revision} - + org.eclipse.digitaltwin.basyx basyx.aasxfileserver-backend-inmemory @@ -489,6 +499,17 @@ basyx.aasxfileserver.component ${revision} + + + org.eclipse.digitaltwin.basyx + basyx.aasregistry-client-native + ${revision} + + + org.eclipse.digitaltwin.basyx + basyx.submodelregistry-client-native + ${revision} + @@ -518,16 +539,16 @@ tests - org.eclipse.digitaltwin.basyx - basyx.submodelservice.component - ${revision} - tests + org.eclipse.digitaltwin.basyx + basyx.submodelservice.component + ${revision} + tests - org.eclipse.digitaltwin.basyx - basyx.submodelservice-http - ${revision} - tests + org.eclipse.digitaltwin.basyx + basyx.submodelservice-http + ${revision} + tests @@ -566,6 +587,12 @@ ${revision} tests + + org.eclipse.digitaltwin.basyx + basyx.submodelrepository-feature-registry-integration + ${revision} + tests + org.eclipse.digitaltwin.basyx @@ -634,6 +661,12 @@ ${revision} tests + + org.eclipse.digitaltwin.basyx + basyx.aasrepository-feature-registry-integration + ${revision} + tests + org.eclipse.digitaltwin.basyx basyx.aasrepository.component @@ -695,7 +728,7 @@ basyx.conceptdescriptionrepository.component ${revision} tests - + org.eclipse.digitaltwin.basyx @@ -732,7 +765,7 @@ basyx.aasdiscoveryservice.component ${revision} tests - + org.eclipse.digitaltwin.basyx