diff --git a/basyx.aasregistry/basyx.aasregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/main/client/factory/AasDescriptorFactory.java b/basyx.aasregistry/basyx.aasregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/main/client/factory/AasDescriptorFactory.java index c1091086e..723b6fbea 100644 --- a/basyx.aasregistry/basyx.aasregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/main/client/factory/AasDescriptorFactory.java +++ b/basyx.aasregistry/basyx.aasregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/main/client/factory/AasDescriptorFactory.java @@ -40,6 +40,7 @@ import org.eclipse.digitaltwin.basyx.aasregistry.client.model.Endpoint; import org.eclipse.digitaltwin.basyx.aasregistry.client.model.ProtocolInformation; import org.eclipse.digitaltwin.basyx.aasregistry.main.client.mapper.AttributeMapper; +import org.eclipse.digitaltwin.basyx.core.RepositoryUrlHelper; import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; /** @@ -196,18 +197,8 @@ private String getProtocol(String endpoint) { private List createAasRepositoryUrls(List aasRepositoryBaseURLs) { List toReturn = new ArrayList<>(aasRepositoryBaseURLs.size()); for (String eachUrl : aasRepositoryBaseURLs) { - toReturn.add(createAasRepositoryUrl(eachUrl)); + toReturn.add(RepositoryUrlHelper.createRepositoryUrl(eachUrl, AAS_REPOSITORY_PATH)); } return toReturn; } - - 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.aasregistry/basyx.aasregistry-client-native/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/main/client/mapper/DummyAasDescriptorFactory.java b/basyx.aasregistry/basyx.aasregistry-client-native/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/main/client/mapper/DummyAasDescriptorFactory.java index 9e2649274..a2d5e8e56 100644 --- a/basyx.aasregistry/basyx.aasregistry-client-native/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/main/client/mapper/DummyAasDescriptorFactory.java +++ b/basyx.aasregistry/basyx.aasregistry-client-native/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/main/client/mapper/DummyAasDescriptorFactory.java @@ -34,6 +34,7 @@ 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.ProtocolInformation; +import org.eclipse.digitaltwin.basyx.core.RepositoryUrlHelper; import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; /** @@ -91,7 +92,7 @@ private static ProtocolInformation createProtocolInformation(String href) { } private static String createHref(String aasId, String aasRepoBaseUrl) { - return String.format("%s/%s", createAasRepositoryUrl(aasRepoBaseUrl), Base64UrlEncodedIdentifier.encodeIdentifier(aasId)); + return String.format("%s/%s", RepositoryUrlHelper.createRepositoryUrl(aasRepoBaseUrl, AAS_REPOSITORY_PATH), Base64UrlEncodedIdentifier.encodeIdentifier(aasId)); } private static String getProtocol(String endpoint) { @@ -101,14 +102,4 @@ private static String getProtocol(String endpoint) { 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/AasRepositoryRegistryLinkDescriptorGenerationTest.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkDescriptorGenerationTest.java new file mode 100644 index 000000000..7c751945b --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkDescriptorGenerationTest.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (C) 2024 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.Collection; +import java.util.List; + +import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultAssetAdministrationShell; +import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi; +import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor; +import org.eclipse.digitaltwin.basyx.aasregistry.main.client.factory.AasDescriptorFactory; +import org.eclipse.digitaltwin.basyx.aasregistry.main.client.mapper.AttributeMapper; +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.SimpleAasRepositoryFactory; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.AasInMemoryBackendProvider; +import org.eclipse.digitaltwin.basyx.aasservice.backend.InMemoryAasServiceFactory; +import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository; +import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.mockito.Mockito; + +/** + * Test suite for {@link RegistryIntegrationAasRepository} feature + */ +@RunWith(Parameterized.class) +public class AasRepositoryRegistryLinkDescriptorGenerationTest { + private static final String DUMMY_IDSHORT = "ExampleMotor"; + private static final String DUMMY_AAS_ID = "customIdentifier"; + + private static final String BASE_URL = "http://localhost:8081"; + + private RegistryIntegrationAasRepository registryIntegrationAasRepository; + private AasRepository mockedAasRepository; + private AasRepositoryRegistryLink mockedRegistryLink; + private AttributeMapper mockedAttributeMapper; + private RegistryAndDiscoveryInterfaceApi mockedRegistryApi; + + @Before + public void setUp() { + mockedAasRepository = getAasRepository(); + mockedRegistryLink = Mockito.mock(AasRepositoryRegistryLink.class); + mockedAttributeMapper = Mockito.mock(AttributeMapper.class); + mockedRegistryApi = Mockito.mock(RegistryAndDiscoveryInterfaceApi.class); + + Mockito.when(mockedRegistryLink.getRegistryApi()).thenReturn(mockedRegistryApi); + + registryIntegrationAasRepository = new RegistryIntegrationAasRepository(mockedAasRepository, mockedRegistryLink, mockedAttributeMapper); + } + + @Parameterized.Parameter(0) + public String externalUrl; + + @Parameterized.Parameter(1) + public String expectedUrl; + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList(new Object[][] { { BASE_URL + "/context", BASE_URL + "/context/shells/" + Base64UrlEncodedIdentifier.encodeIdentifier(DUMMY_AAS_ID) }, + { BASE_URL, BASE_URL + "/shells/" + Base64UrlEncodedIdentifier.encodeIdentifier(DUMMY_AAS_ID) }, { BASE_URL + "/", BASE_URL + "/shells/" + Base64UrlEncodedIdentifier.encodeIdentifier(DUMMY_AAS_ID) }, + { BASE_URL + "/context/", BASE_URL + "/context/shells/" + Base64UrlEncodedIdentifier.encodeIdentifier(DUMMY_AAS_ID) } }); + } + + @Test + public void testExternalUrl() { + Mockito.when(mockedRegistryLink.getAasRepositoryBaseURLs()).thenReturn(List.of(externalUrl)); + + AssetAdministrationShellDescriptor descriptor = createAndRetrieveDescriptor(createDummyAas()); + String actualUrl = descriptor.getEndpoints().get(0).getProtocolInformation().getHref(); + + assertEquals(expectedUrl, actualUrl); + } + + private AssetAdministrationShellDescriptor createAndRetrieveDescriptor(AssetAdministrationShell shell) { + registryIntegrationAasRepository.createAas(shell); + + AasDescriptorFactory descriptorFactory = new AasDescriptorFactory(shell, mockedRegistryLink.getAasRepositoryBaseURLs(), mockedAttributeMapper); + return descriptorFactory.create(); + } + + private AssetAdministrationShell createDummyAas() { + return new DefaultAssetAdministrationShell.Builder() + .id(DUMMY_AAS_ID) + .idShort(DUMMY_IDSHORT) + .build(); + } + + protected AasRepository getAasRepository() { + return new SimpleAasRepositoryFactory(new AasInMemoryBackendProvider(), new InMemoryAasServiceFactory(new InMemoryFileRepository())).create(); + } + +} diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTestSuite.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTestSuite.java index 19629dcb7..09c55e696 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTestSuite.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTestSuite.java @@ -40,6 +40,7 @@ import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor; import org.eclipse.digitaltwin.basyx.aasregistry.client.model.GetAssetAdministrationShellDescriptorsResult; import org.eclipse.digitaltwin.basyx.aasregistry.main.client.mapper.DummyAasDescriptorFactory; +import org.eclipse.digitaltwin.basyx.core.RepositoryUrlHelper; import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; import org.eclipse.digitaltwin.basyx.http.serialization.BaSyxHttpTestUtils; import org.junit.After; @@ -129,11 +130,12 @@ private String getAas1JSONString() throws FileNotFoundException, IOException { } private CloseableHttpResponse createAasOnRepo(String aasJsonContent) throws IOException { - return BaSyxHttpTestUtils.executePostOnURL(createAasRepositoryUrl(getFirstAasRepoBaseUrl()), aasJsonContent); + String url = RepositoryUrlHelper.createRepositoryUrl(getFirstAasRepoBaseUrl(), AAS_REPOSITORY_PATH); + return BaSyxHttpTestUtils.executePostOnURL(RepositoryUrlHelper.createRepositoryUrl(getFirstAasRepoBaseUrl(), AAS_REPOSITORY_PATH), aasJsonContent); } private String getSpecificAasAccessURL(String aasId) { - return createAasRepositoryUrl(getFirstAasRepoBaseUrl()) + "/" + return RepositoryUrlHelper.createRepositoryUrl(getFirstAasRepoBaseUrl(), AAS_REPOSITORY_PATH) + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(aasId); } @@ -141,13 +143,4 @@ private String getFirstAasRepoBaseUrl() { return getAasRepoBaseUrls()[0]; } - 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()); - } - } - -} +} \ No newline at end of file diff --git a/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/RepositoryUrlHelper.java b/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/RepositoryUrlHelper.java new file mode 100644 index 000000000..a046bfcc2 --- /dev/null +++ b/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/RepositoryUrlHelper.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (C) 2024 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; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; + +public final class RepositoryUrlHelper { + + private RepositoryUrlHelper() { + } + + public static String createRepositoryUrl(String baseUrl, String additionalPath) { + try { + URI baseUri = new URI(baseUrl); + + String[] basePathSegments = baseUri.getPath().replaceAll("^/|/$", "").split("/"); + String[] additionalPathSegments = additionalPath != null ? additionalPath.replaceAll("^/|/$", "").split("/") : new String[0]; + + StringBuilder fullPath = new StringBuilder(); + for (String segment : basePathSegments) { + if (!segment.isEmpty()) { + fullPath.append("/").append(segment); + } + } + for (String segment : additionalPathSegments) { + if (!segment.isEmpty()) { + fullPath.append("/").append(segment); + } + } + + URI finalUri = new URI(baseUri.getScheme(), null, baseUri.getHost(), baseUri.getPort(), fullPath.toString(), null, null); + + return finalUri.toURL().toString(); + } catch (URISyntaxException | MalformedURLException e) { + throw new RuntimeException("The Base URL or additional path is malformed.\n" + e.getMessage(), e); + } + } + +} diff --git a/basyx.common/basyx.core/src/test/java/org/eclipse/digitaltwin/basyx/core/TestRepositoryUrlHelper.java b/basyx.common/basyx.core/src/test/java/org/eclipse/digitaltwin/basyx/core/TestRepositoryUrlHelper.java new file mode 100644 index 000000000..5a7a473a9 --- /dev/null +++ b/basyx.common/basyx.core/src/test/java/org/eclipse/digitaltwin/basyx/core/TestRepositoryUrlHelper.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (C) 2024 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; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class TestRepositoryUrlHelper { + + private static final String BASE_URL = "http://localhost:8081"; + private static final String BASE_URL_WITH_CONTEXT = "http://localhost:8081/context"; + private static final String ADDITIONAL_PATH = "shells"; + + private static final String EXPECTED_URL = BASE_URL + "/" + ADDITIONAL_PATH; + private static final String EXPECTED_URL_WITH_CONTEXT = BASE_URL_WITH_CONTEXT + "/" + ADDITIONAL_PATH; + + @Test + public void testUrlWithTrailingSlashAndPathNameWithNoLeadingSlash() { + assertEquals(EXPECTED_URL, RepositoryUrlHelper.createRepositoryUrl(BASE_URL + "/", ADDITIONAL_PATH)); + } + + @Test + public void testUrlWithoutTrailingSlashAndPathNameWithNoLeadingSlash() { + assertEquals(EXPECTED_URL, RepositoryUrlHelper.createRepositoryUrl(BASE_URL, ADDITIONAL_PATH)); + } + + @Test + public void testUrlWithTrailingSlashAndPathNameWithLeadingSlash() { + assertEquals(EXPECTED_URL, RepositoryUrlHelper.createRepositoryUrl(BASE_URL + "/", "/" + ADDITIONAL_PATH)); + } + + @Test + public void testUrlWithoutTrailingSlashAndPathNameWithLeadingSlash() { + assertEquals(EXPECTED_URL, RepositoryUrlHelper.createRepositoryUrl(BASE_URL, "/" + ADDITIONAL_PATH)); + } + + @Test + public void testUrlWithContextAndTrailingSlashAndPathNameWithLeadingSlash() { + assertEquals(EXPECTED_URL_WITH_CONTEXT, + RepositoryUrlHelper.createRepositoryUrl(BASE_URL_WITH_CONTEXT + "/", "/" + ADDITIONAL_PATH)); + } + + @Test + public void testUrlWithContextAndNoTrailingSlashAndPathNameWithLeadingSlash() { + assertEquals(EXPECTED_URL_WITH_CONTEXT, + RepositoryUrlHelper.createRepositoryUrl(BASE_URL_WITH_CONTEXT, "/" + ADDITIONAL_PATH)); + } + + @Test + public void testUrlWithContextAndTrailingSlashAndPathNameWithNoLeadingSlash() { + assertEquals(EXPECTED_URL_WITH_CONTEXT, + RepositoryUrlHelper.createRepositoryUrl(BASE_URL_WITH_CONTEXT + "/", ADDITIONAL_PATH)); + } + + @Test + public void testUrlWithContextAndNoTrailingSlashAndPathNameWithNoLeadingSlash() { + assertEquals(EXPECTED_URL_WITH_CONTEXT, + RepositoryUrlHelper.createRepositoryUrl(BASE_URL_WITH_CONTEXT, ADDITIONAL_PATH)); + } +} diff --git a/basyx.submodelregistry/basyx.submodelregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/client/factory/SubmodelDescriptorFactory.java b/basyx.submodelregistry/basyx.submodelregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/client/factory/SubmodelDescriptorFactory.java index 7ba46b67d..e5487241c 100644 --- a/basyx.submodelregistry/basyx.submodelregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/client/factory/SubmodelDescriptorFactory.java +++ b/basyx.submodelregistry/basyx.submodelregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/client/factory/SubmodelDescriptorFactory.java @@ -36,6 +36,7 @@ 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.core.RepositoryUrlHelper; import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; import org.eclipse.digitaltwin.basyx.submodelregistry.client.mapper.AttributeMapper; import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Endpoint; @@ -187,18 +188,8 @@ private String getProtocol(String endpoint) { private List createSubmodelRepositoryUrls(List submodelRepositoryBaseURLs) { List toReturn = new ArrayList<>(submodelRepositoryBaseURLs.size()); for (String eachUrl : submodelRepositoryBaseURLs) { - toReturn.add(createSubmodelRepositoryUrl(eachUrl)); + toReturn.add(RepositoryUrlHelper.createRepositoryUrl(eachUrl, SUBMODEL_REPOSITORY_PATH)); } return toReturn; } - - 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.submodelregistry/basyx.submodelregistry-client-native/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/client/mapper/DummySubmodelDescriptorFactory.java b/basyx.submodelregistry/basyx.submodelregistry-client-native/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/client/mapper/DummySubmodelDescriptorFactory.java index 8dfaf8656..7a40755f0 100644 --- a/basyx.submodelregistry/basyx.submodelregistry-client-native/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/client/mapper/DummySubmodelDescriptorFactory.java +++ b/basyx.submodelregistry/basyx.submodelregistry-client-native/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/client/mapper/DummySubmodelDescriptorFactory.java @@ -31,6 +31,7 @@ import java.util.LinkedList; import java.util.List; +import org.eclipse.digitaltwin.basyx.core.RepositoryUrlHelper; import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Endpoint; import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.Key; @@ -96,7 +97,7 @@ private static ProtocolInformation createProtocolInformation(String href) { } private static String createHref(String smId, String smRepoBaseUrl) { - return String.format("%s/%s", createSubmodelRepositoryUrl(smRepoBaseUrl), Base64UrlEncodedIdentifier.encodeIdentifier(smId)); + return String.format("%s/%s", RepositoryUrlHelper.createRepositoryUrl(smRepoBaseUrl, SUBMODEL_REPOSITORY_PATH), Base64UrlEncodedIdentifier.encodeIdentifier(smId)); } private static String getProtocol(String endpoint) { @@ -106,13 +107,4 @@ private static String getProtocol(String endpoint) { 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-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/TestAuthorizedSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/TestAuthorizedSubmodelRepository.java index f025a03a4..b65f1e8ce 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/TestAuthorizedSubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/TestAuthorizedSubmodelRepository.java @@ -46,6 +46,7 @@ import org.eclipse.digitaltwin.basyx.authorization.DummyCredentialStore; import org.eclipse.digitaltwin.basyx.authorization.jwt.JwtTokenDecoder; import org.eclipse.digitaltwin.basyx.authorization.jwt.PublicKeyUtils; +import org.eclipse.digitaltwin.basyx.core.RepositoryUrlHelper; import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; import org.eclipse.digitaltwin.basyx.http.serialization.BaSyxHttpTestUtils; import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; @@ -200,7 +201,7 @@ public void getSubmodelWithCorrectRoleAndSpecificSubmodelElementPermission() thr public void createSubmodelWithCorrectRoleAndPermission() throws IOException { String accessToken = getAccessToken(DummyCredentialStore.BASYX_CREATOR_CREDENTIAL); - CloseableHttpResponse retrievalResponse = createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), accessToken); + CloseableHttpResponse retrievalResponse = createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), accessToken); assertEquals(HttpStatus.CREATED.value(), retrievalResponse.getCode()); deleteElementWithAuthorization(getSpecificSubmodelAccessURL(SPECIFIC_SUBMODEL_ID_2), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); @@ -210,13 +211,13 @@ public void createSubmodelWithCorrectRoleAndPermission() throws IOException { public void createSubmodelWithInsufficientPermissionRole() throws IOException { String accessToken = getAccessToken(DummyCredentialStore.BASYX_READER_CREDENTIAL); - CloseableHttpResponse retrievalResponse = createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), accessToken); + CloseableHttpResponse retrievalResponse = createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), accessToken); assertEquals(HttpStatus.FORBIDDEN.value(), retrievalResponse.getCode()); } @Test public void createSubmodelWithNoAuthorization() throws IOException { - CloseableHttpResponse retrievalResponse = createElementOnRepositoryWithNoAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON)); + CloseableHttpResponse retrievalResponse = createElementOnRepositoryWithNoAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON)); assertEquals(HttpStatus.UNAUTHORIZED.value(), retrievalResponse.getCode()); } @@ -262,7 +263,7 @@ public void updateSubmodelWithNoAuthorization() throws IOException { @Test public void deleteSubmodelWithCorrectRoleAndPermission() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); String accessToken = getAccessToken(DummyCredentialStore.BASYX_DELETER_CREDENTIAL); @@ -276,7 +277,7 @@ public void deleteSubmodelWithCorrectRoleAndPermission() throws IOException { @Test public void deleteSubmodelWithCorrectRoleAndSpecificSubmodelPermission() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); String accessToken = getAccessToken(DummyCredentialStore.BASYX_DELETER_TWO_CREDENTIAL); @@ -424,7 +425,7 @@ public void getSubmodelElementValueWithCorrectRoleAndPermission() throws IOExcep DummyCredential dummyCredential = DummyCredentialStore.BASYX_READER_CREDENTIAL; String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); - + CloseableHttpResponse retrievalResponse = getElementWithAuthorization(getSpecificSubmodelElementValueAccessURL(SPECIFIC_SUBMODEL_ID, SUBMODEL_ELEMENT_IDSHORT_PATH), accessToken); assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode()); } @@ -627,7 +628,7 @@ public void updateSubmodelElementWithNoAuthorization() throws IOException { @Test public void deleteSubmodelElementWithCorrectRoleAndPermission() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); String accessToken = getAccessToken(DummyCredentialStore.BASYX_UPDATER_CREDENTIAL); @@ -641,7 +642,7 @@ public void deleteSubmodelElementWithCorrectRoleAndPermission() throws IOExcepti @Test public void deleteSubmodelElementWithCorrectRoleAndSpecificSMEPermission() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); String accessToken = getAccessToken(DummyCredentialStore.BASYX_SME_UPDATER_THREE_CREDENTIAL); @@ -655,7 +656,7 @@ public void deleteSubmodelElementWithCorrectRoleAndSpecificSMEPermission() throw @Test public void deleteSubmodelElementWithCorrectRoleAndUnauthorizedSpecificSME() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); String accessToken = getAccessToken(DummyCredentialStore.BASYX_SME_UPDATER_THREE_CREDENTIAL); @@ -669,7 +670,7 @@ public void deleteSubmodelElementWithCorrectRoleAndUnauthorizedSpecificSME() thr @Test public void deleteSubmodelElementWithInsufficientPermissionRole() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); String accessToken = getAccessToken(DummyCredentialStore.BASYX_CREATOR_CREDENTIAL); @@ -683,7 +684,7 @@ public void deleteSubmodelElementWithInsufficientPermissionRole() throws IOExcep @Test public void deleteSubmodelElementWithNoAuthorization() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); CloseableHttpResponse retrievalResponse = deleteElementWithNoAuthorization(getSpecificSubmodelElementAccessURL(SPECIFIC_SUBMODEL_ID_2, SUBMODEL_ELEMENT_IDSHORT_PATH_2)); @@ -851,7 +852,7 @@ public void getSubmodelByMetadataWithNoAuthorization() throws IOException { @Test public void getFileWithCorrectRoleAndPermission() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); java.io.File file = ResourceUtils.getFile("classpath:" + FILE_NAME); @@ -869,7 +870,7 @@ public void getFileWithCorrectRoleAndPermission() throws IOException { @Test public void getFileWithAuthorizedSpecificSubmodelAndSpecificSME() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); java.io.File file = ResourceUtils.getFile("classpath:" + FILE_NAME); @@ -887,7 +888,7 @@ public void getFileWithAuthorizedSpecificSubmodelAndSpecificSME() throws IOExcep @Test public void getFileWithInsufficientPermissionRole() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); java.io.File file = ResourceUtils.getFile("classpath:" + FILE_NAME); @@ -905,7 +906,7 @@ public void getFileWithInsufficientPermissionRole() throws IOException { @Test public void getFileWithUnauthorizedSpecificSME() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); DummyCredential dummyCredential = DummyCredentialStore.BASYX_FILE_SME_READER_CREDENTIAL; @@ -919,7 +920,7 @@ public void getFileWithUnauthorizedSpecificSME() throws IOException { @Test public void getFileWithNoAuthorization() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); CloseableHttpResponse retrievalResponse = getElementWithNoAuthorization(getSMEFileDownloadURL(SPECIFIC_SUBMODEL_ID_2, FILE_SUBMODEL_ELEMENT_IDSHORT_PATH)); assertEquals(HttpStatus.UNAUTHORIZED.value(), retrievalResponse.getCode()); @@ -929,7 +930,7 @@ public void getFileWithNoAuthorization() throws IOException { @Test public void setFileWithCorrectRoleAndPermission() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); java.io.File file = ResourceUtils.getFile("classpath:" + FILE_NAME); @@ -945,7 +946,7 @@ public void setFileWithCorrectRoleAndPermission() throws IOException { @Test public void setFileWithAuthorizedSpecificSubmodelAndSpecificSME() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); java.io.File file = ResourceUtils.getFile("classpath:" + FILE_NAME); @@ -961,7 +962,7 @@ public void setFileWithAuthorizedSpecificSubmodelAndSpecificSME() throws IOExcep @Test public void setFileWithInsufficientPermissionRole() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); java.io.File file = ResourceUtils.getFile("classpath:" + FILE_NAME); @@ -977,7 +978,7 @@ public void setFileWithInsufficientPermissionRole() throws IOException { @Test public void setFileWithUnauthorizedSpecificSME() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); java.io.File file = ResourceUtils.getFile("classpath:" + FILE_NAME); @@ -993,7 +994,7 @@ public void setFileWithUnauthorizedSpecificSME() throws IOException { @Test public void setFileWithNoAuthorization() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); java.io.File file = ResourceUtils.getFile("classpath:" + FILE_NAME); @@ -1005,7 +1006,7 @@ public void setFileWithNoAuthorization() throws IOException { @Test public void deleteFileWithCorrectRoleAndPermission() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); java.io.File file = ResourceUtils.getFile("classpath:" + FILE_NAME); @@ -1023,7 +1024,7 @@ public void deleteFileWithCorrectRoleAndPermission() throws IOException { @Test public void deleteFileWithAuthorizedSpecificSubmodelAndSpecificSME() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); java.io.File file = ResourceUtils.getFile("classpath:" + FILE_NAME); @@ -1041,7 +1042,7 @@ public void deleteFileWithAuthorizedSpecificSubmodelAndSpecificSME() throws IOEx @Test public void deleteFileWithInsufficientPermissionRole() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); java.io.File file = ResourceUtils.getFile("classpath:" + FILE_NAME); @@ -1059,7 +1060,7 @@ public void deleteFileWithInsufficientPermissionRole() throws IOException { @Test public void deleteFileWithUnauthorizedSpecificSME() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); java.io.File file = ResourceUtils.getFile("classpath:" + FILE_NAME); @@ -1077,7 +1078,7 @@ public void deleteFileWithUnauthorizedSpecificSME() throws IOException { @Test public void deleteFileWithNoAuthorization() throws IOException { - createElementOnRepositoryWithAuthorization(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); + createElementOnRepositoryWithAuthorization(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), getStringFromFile(SUBMODEL_SIMPLE_2_JSON), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL)); CloseableHttpResponse retrievalResponse = deleteElementWithNoAuthorization(getSMEFileDownloadURL(SPECIFIC_SUBMODEL_ID_2, FILE_SUBMODEL_ELEMENT_IDSHORT_PATH)); assertEquals(HttpStatus.UNAUTHORIZED.value(), retrievalResponse.getCode()); @@ -1185,7 +1186,7 @@ private String getAccessToken(DummyCredential dummyCredential) { } private CloseableHttpResponse getAllSubmodelsWithAuthorization(String accessToken) throws IOException { - return BaSyxHttpTestUtils.executeAuthorizedGetOnURL(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl), accessToken); + return BaSyxHttpTestUtils.executeAuthorizedGetOnURL(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH), accessToken); } private CloseableHttpResponse getAllSubmodelElementsWithAuthorization(String submodelId, String accessToken) throws IOException { @@ -1193,7 +1194,7 @@ private CloseableHttpResponse getAllSubmodelElementsWithAuthorization(String sub } private CloseableHttpResponse getAllSubmodelsNoAuthorization() throws IOException { - return BaSyxHttpTestUtils.executeGetOnURL(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl)); + return BaSyxHttpTestUtils.executeGetOnURL(RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH)); } private CloseableHttpResponse getSpecificSubmodelElementNoAuthorization(String submodelId, String submodelElementIdShortPath) throws IOException { @@ -1217,7 +1218,7 @@ private CloseableHttpResponse getElementWithNoAuthorization(String url) throws I } private String getSpecificSubmodelAccessURL(String submodelId) { - return createSubmodelRepositoryUrl(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl)) + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(submodelId); + return RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH) + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(submodelId); } private String getSpecificSubmodelValueOnlyAccessURL(String submodelId) { @@ -1229,7 +1230,7 @@ private String getSpecificSubmodelMetadataAccessURL(String submodelId) { } private String getSpecificSubmodelElementAccessURL(String submodelId, String submodelElementIdShortPath) { - return createSubmodelRepositoryUrl(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl)) + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(submodelId) + "/submodel-elements/" + submodelElementIdShortPath; + return RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH) + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(submodelId) + "/submodel-elements/" + submodelElementIdShortPath; } private String getSpecificSubmodelElementValueAccessURL(String submodelId, String submodelElementIdShortPath) { @@ -1257,7 +1258,7 @@ private CloseableHttpResponse requestOperationInvocationNoAuthorization(String u } protected String getAllSubmodelElementsAccessURL(String submodelId) { - return createSubmodelRepositoryUrl(createSubmodelRepositoryUrl(submodelRepositoryBaseUrl)) + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(submodelId) + "/submodel-elements"; + return RepositoryUrlHelper.createRepositoryUrl(submodelRepositoryBaseUrl, SUBMODEL_REPOSITORY_PATH) + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(submodelId) + "/submodel-elements"; } private static CloseableHttpResponse createElementOnRepositoryWithAuthorization(String url, String submodelJsonContent, String accessToken) throws IOException { @@ -1302,15 +1303,6 @@ private static String getAdminAccessToken() { return tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); } - private static 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. " + e.getMessage()); - } - } - private String getJSONValueAsString(String fileName) throws FileNotFoundException, IOException { return BaSyxHttpTestUtils.readJSONStringFromClasspath(fileName); } diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkDescriptorGenerationTest.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkDescriptorGenerationTest.java new file mode 100644 index 000000000..a414b8108 --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkDescriptorGenerationTest.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (C) 2024 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.Collection; +import java.util.List; + +import org.eclipse.digitaltwin.aas4j.v3.model.Submodel; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodel; +import org.eclipse.digitaltwin.basyx.client.internal.ApiException; +import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository; +import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.api.SubmodelRegistryApi; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.factory.SubmodelDescriptorFactory; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.mapper.AttributeMapper; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.SubmodelDescriptor; +import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelInMemoryBackendProvider; +import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; +import org.eclipse.digitaltwin.basyx.submodelrepository.backend.SimpleSubmodelRepositoryFactory; +import org.eclipse.digitaltwin.basyx.submodelservice.InMemorySubmodelServiceFactory; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.mockito.Mockito; + +/** + * Test suite for {@link RegistryIntegrationAasRepository} feature + */ +@RunWith(Parameterized.class) +public class SubmodelRepositoryRegistryLinkDescriptorGenerationTest { + private static final String DUMMY_SUBMODEL_IDSHORT = "TechnicalData"; + private static final String DUMMY_SUBMODEL_ID = "7A7104BDAB57E184"; + + private static final String BASE_URL = "http://localhost:8081"; + + private RegistryIntegrationSubmodelRepository registryIntegrationSubmodelRepository; + private SubmodelRepository mockedSubmodelRepository; + private SubmodelRepositoryRegistryLink mockedRegistryLink; + private AttributeMapper mockedAttributeMapper; + private SubmodelRegistryApi mockedRegistryApi; + + @Before + public void setUp() { + mockedSubmodelRepository = getSubmodelRepository(); + mockedRegistryLink = Mockito.mock(SubmodelRepositoryRegistryLink.class); + mockedAttributeMapper = Mockito.mock(AttributeMapper.class); + mockedRegistryApi = Mockito.mock(SubmodelRegistryApi.class); + + Mockito.when(mockedRegistryLink.getRegistryApi()).thenReturn(mockedRegistryApi); + + registryIntegrationSubmodelRepository = new RegistryIntegrationSubmodelRepository(mockedSubmodelRepository, mockedRegistryLink, mockedAttributeMapper); + } + + @Parameterized.Parameter(0) + public String externalUrl; + + @Parameterized.Parameter(1) + public String expectedUrl; + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList(new Object[][] { { BASE_URL + "/context", BASE_URL + "/context/submodels/" + Base64UrlEncodedIdentifier.encodeIdentifier(DUMMY_SUBMODEL_ID) }, + { BASE_URL, BASE_URL + "/submodels/" + Base64UrlEncodedIdentifier.encodeIdentifier(DUMMY_SUBMODEL_ID) }, { BASE_URL + "/", BASE_URL + "/submodels/" + Base64UrlEncodedIdentifier.encodeIdentifier(DUMMY_SUBMODEL_ID) }, + { BASE_URL + "/context/", BASE_URL + "/context/submodels/" + Base64UrlEncodedIdentifier.encodeIdentifier(DUMMY_SUBMODEL_ID) } }); + } + + @Test + public void testExternalUrl() throws ApiException { + Mockito.when(mockedRegistryLink.getSubmodelRepositoryBaseURLs()).thenReturn(List.of(externalUrl)); + + SubmodelDescriptor descriptor = createAndRetrieveDescriptor(createDummySubmodel()); + String actualUrl = descriptor.getEndpoints().get(0).getProtocolInformation().getHref(); + + assertEquals(expectedUrl, actualUrl); + } + + private SubmodelDescriptor createAndRetrieveDescriptor(Submodel submodel) throws ApiException { + registryIntegrationSubmodelRepository.createSubmodel(submodel); + + SubmodelDescriptorFactory descriptorFactory = new SubmodelDescriptorFactory(submodel, mockedRegistryLink.getSubmodelRepositoryBaseURLs(), mockedAttributeMapper); + return descriptorFactory.create(); + } + + private Submodel createDummySubmodel() { + return new DefaultSubmodel.Builder().id(DUMMY_SUBMODEL_ID).idShort(DUMMY_SUBMODEL_IDSHORT).build(); + } + + protected SubmodelRepository getSubmodelRepository() { + return new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory(new InMemoryFileRepository())).create(); + } + +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkTestSuite.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkTestSuite.java index 0e63d1de7..2933b418f 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkTestSuite.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkTestSuite.java @@ -30,12 +30,11 @@ 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.apache.hc.core5.http.ParseException; +import org.eclipse.digitaltwin.basyx.core.RepositoryUrlHelper; import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; import org.eclipse.digitaltwin.basyx.http.serialization.BaSyxHttpTestUtils; import org.eclipse.digitaltwin.basyx.submodelregistry.client.ApiException; @@ -54,9 +53,8 @@ * @author danish */ public abstract class SubmodelRepositoryRegistryLinkTestSuite { + private static final String SUBMODEL_REPOSITORY_PATH = "submodels"; - private static final String SUBMODEL_REPOSITORY_PATH = "/submodels"; - private static final String DUMMY_SUBMODEL_IDSHORT = "TechnicalData"; private static final String DUMMY_SUBMODEL_ID = "7A7104BDAB57E184"; @@ -150,7 +148,7 @@ private String getExpectedSubmodel() throws FileNotFoundException, IOException { } private CloseableHttpResponse createSubmodelOnRepo(String submodelJsonContent) throws IOException { - return BaSyxHttpTestUtils.executePostOnURL(createSubmodelRepositoryUrl(getFirstSubmodeRepoBaseUrl()), submodelJsonContent); + return BaSyxHttpTestUtils.executePostOnURL(RepositoryUrlHelper.createRepositoryUrl(getFirstSubmodeRepoBaseUrl(), SUBMODEL_REPOSITORY_PATH), submodelJsonContent); } private String getFirstSubmodeRepoBaseUrl() { @@ -164,16 +162,6 @@ private CloseableHttpResponse createSubmodelElementOnRepo(String submodelElement } private String getSpecificSubmodelAccessURL(String submodelId) { - return createSubmodelRepositoryUrl(getFirstSubmodeRepoBaseUrl()) + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(submodelId); - } - - 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()); - } + return RepositoryUrlHelper.createRepositoryUrl(getFirstSubmodeRepoBaseUrl(), SUBMODEL_REPOSITORY_PATH) + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(submodelId); } - }