diff --git a/basyx.aasrepository/basyx.aasrepository-backend-inmemory/pom.xml b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/pom.xml
index 2d6a74882..78154f227 100644
--- a/basyx.aasrepository/basyx.aasrepository-backend-inmemory/pom.xml
+++ b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/pom.xml
@@ -1,5 +1,6 @@
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
diff --git a/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/AasInMemoryBackend.java b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/AasInMemoryBackend.java
index 42ceba322..1aaa6a7ef 100644
--- a/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/AasInMemoryBackend.java
+++ b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/AasInMemoryBackend.java
@@ -111,5 +111,7 @@ public void deleteAll(Iterable extends AssetAdministrationShell> entities) {
public void deleteAll() {
inMemoryStore.clear();
}
+
+
}
diff --git a/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/TestInMemoryAasRepository.java b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/TestInMemoryAasRepository.java
index df8d4aa51..490c8abb2 100644
--- a/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/TestInMemoryAasRepository.java
+++ b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/inmemory/TestInMemoryAasRepository.java
@@ -33,7 +33,6 @@
import org.eclipse.digitaltwin.basyx.aasrepository.backend.AasBackendProvider;
import org.eclipse.digitaltwin.basyx.aasrepository.backend.CrudAasRepository;
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.junit.Test;
diff --git a/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/test/resources/BaSyx-Logo-1.png b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/test/resources/BaSyx-Logo-1.png
new file mode 100644
index 000000000..da613e94c
Binary files /dev/null and b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/test/resources/BaSyx-Logo-1.png differ
diff --git a/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/test/resources/BaSyx-Logo-2.png b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/test/resources/BaSyx-Logo-2.png
new file mode 100644
index 000000000..819b68188
Binary files /dev/null and b/basyx.aasrepository/basyx.aasrepository-backend-inmemory/src/test/resources/BaSyx-Logo-2.png differ
diff --git a/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/resources/BaSyx-Logo-1.png b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/resources/BaSyx-Logo-1.png
new file mode 100644
index 000000000..da613e94c
Binary files /dev/null and b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/resources/BaSyx-Logo-1.png differ
diff --git a/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/resources/BaSyx-Logo-2.png b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/resources/BaSyx-Logo-2.png
new file mode 100644
index 000000000..da613e94c
Binary files /dev/null and b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/test/resources/BaSyx-Logo-2.png differ
diff --git a/basyx.aasrepository/basyx.aasrepository-backend/pom.xml b/basyx.aasrepository/basyx.aasrepository-backend/pom.xml
index 0fd9d552f..e3938f105 100644
--- a/basyx.aasrepository/basyx.aasrepository-backend/pom.xml
+++ b/basyx.aasrepository/basyx.aasrepository-backend/pom.xml
@@ -20,5 +20,9 @@
org.springframework.data
spring-data-commons
+
+ commons-io
+ commons-io
+
\ No newline at end of file
diff --git a/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/AASThumbnailHandler.java b/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/AASThumbnailHandler.java
new file mode 100644
index 000000000..a751d9af4
--- /dev/null
+++ b/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/AASThumbnailHandler.java
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * 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.backend;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Paths;
+
+import org.apache.commons.io.IOUtils;
+import org.eclipse.digitaltwin.aas4j.v3.model.AssetInformation;
+import org.eclipse.digitaltwin.aas4j.v3.model.Resource;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultResource;
+import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository;
+import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.FileHandlingException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AASThumbnailHandler {
+
+ private static Logger logger = LoggerFactory.getLogger(AASThumbnailHandler.class);
+
+ public static void updateThumbnail(AasRepository aasRepo, String aasId, String contentType, String filePath) {
+ AssetInformation assetInfor = aasRepo.getAssetInformation(aasId);
+ assetInfor.getDefaultThumbnail().setContentType(contentType);
+ assetInfor.getDefaultThumbnail().setPath(filePath);
+ aasRepo.setAssetInformation(aasId, assetInfor);
+ }
+
+ public static void setNewThumbnail(AasRepository aasRepo, String aasId, String contentType, String filePath) {
+ Resource resource = new DefaultResource();
+ resource.setContentType(contentType);
+ resource.setPath(filePath);
+ AssetInformation assetInfor = aasRepo.getAssetInformation(aasId);
+ assetInfor.setDefaultThumbnail(resource);
+ aasRepo.setAssetInformation(aasId, assetInfor);
+ }
+
+ public static void throwIfFileDoesNotExist(String aasId, Resource resource) {
+ if (resource == null)
+ throw new FileDoesNotExistException(aasId);
+
+ String filePath = resource.getPath();
+ throwIfFilePathIsNotValid(aasId, filePath);
+ }
+
+ public static String createFilePath(String tmpDirectory, String aasId, String fileName) {
+ return tmpDirectory + "/" + aasId + "-" + "Thumbnail" + "-" + fileName;
+ }
+
+ public static void createFileAtSpecifiedPath(String fileName, InputStream inputStream, String filePath) {
+ java.io.File targetFile = new java.io.File(filePath);
+
+ try (FileOutputStream outStream = new FileOutputStream(targetFile)) {
+ IOUtils.copy(inputStream, outStream);
+ } catch (IOException e) {
+ throw new FileHandlingException(fileName);
+ }
+ }
+
+ public static void deleteExistingFile(String path) {
+ if (path == null || path.isEmpty())
+ return;
+
+ try {
+ Files.deleteIfExists(Paths.get(path, ""));
+ } catch (IOException e) {
+ logger.error("Unable to delete the file having path '{}'", path);
+ }
+ }
+
+ public static String getTemporaryDirectoryPath() {
+ String tempDirectoryPath = "";
+ try {
+ tempDirectoryPath = Files.createTempDirectory("basyx-temp-thumbnail").toAbsolutePath().toString();
+ } catch (IOException e) {
+ logger.error("Unable to create file in the temporary path.");
+ }
+ return tempDirectoryPath;
+ }
+
+ private static void throwIfFilePathIsNotValid(String aasId, String filePath) {
+ if (filePath.isEmpty())
+ throw new FileDoesNotExistException(aasId);
+ try {
+ Paths.get(filePath);
+ } catch (InvalidPathException | NullPointerException ex) {
+ throw new FileDoesNotExistException(aasId);
+ }
+ }
+}
diff --git a/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/CrudAasRepository.java b/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/CrudAasRepository.java
index 7addf32b0..6f1592450 100644
--- a/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/CrudAasRepository.java
+++ b/basyx.aasrepository/basyx.aasrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/CrudAasRepository.java
@@ -24,6 +24,8 @@
******************************************************************************/
package org.eclipse.digitaltwin.basyx.aasrepository.backend;
+import java.io.File;
+import java.io.InputStream;
import java.util.List;
import java.util.TreeMap;
import java.util.stream.Collectors;
@@ -32,6 +34,7 @@
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.aas4j.v3.model.Resource;
import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository;
import org.eclipse.digitaltwin.basyx.aasservice.AasService;
import org.eclipse.digitaltwin.basyx.aasservice.AasServiceFactory;
@@ -45,9 +48,10 @@
import org.springframework.data.repository.CrudRepository;
/**
- * Default Implementation for the {@link AasRepository} based on Spring {@link CrudRepository}
+ * Default Implementation for the {@link AasRepository} based on Spring
+ * {@link CrudRepository}
*
- * @author mateusmolina, despen
+ * @author mateusmolina, despen, zhangzai
*
*/
public class CrudAasRepository implements AasRepository {
@@ -57,7 +61,7 @@ public class CrudAasRepository implements AasRepository {
private AasServiceFactory aasServiceFactory;
private String aasRepositoryName = null;
-
+
public CrudAasRepository(AasBackendProvider aasBackendProvider, AasServiceFactory aasServiceFactory) {
this.aasBackend = aasBackendProvider.getCrudRepository();
this.aasServiceFactory = aasServiceFactory;
@@ -65,7 +69,7 @@ public CrudAasRepository(AasBackendProvider aasBackendProvider, AasServiceFactor
public CrudAasRepository(AasBackendProvider aasBackendProvider, AasServiceFactory aasServiceFactory, @Value("${basyx.aasrepo.name:aas-repo}") String aasRepositoryName) {
this(aasBackendProvider, aasServiceFactory);
-
+
this.aasRepositoryName = aasRepositoryName;
}
@@ -147,6 +151,44 @@ public AssetInformation getAssetInformation(String aasId) throws ElementDoesNotE
return getAasServiceOrThrow(aasId).getAssetInformation();
}
+
+ @Override
+ public String getName() {
+ return aasRepositoryName == null ? AasRepository.super.getName() : aasRepositoryName;
+ }
+
+ @Override
+ public File getThumbnail(String aasId) {
+ Resource resource = getAssetInformation(aasId).getDefaultThumbnail();
+
+ AASThumbnailHandler.throwIfFileDoesNotExist(aasId, resource);
+ String filePath = resource.getPath();
+ return new File(filePath);
+ }
+
+ @Override
+ public void setThumbnail(String aasId, String fileName, String contentType, InputStream inputStream) {
+ Resource thumbnail = getAssetInformation(aasId).getDefaultThumbnail();
+
+ if (thumbnail != null) {
+ updateThumbnailFile(aasId, fileName, contentType, inputStream, thumbnail);
+ return;
+ }
+
+ String filePath = createFile(aasId, fileName, inputStream);
+ AASThumbnailHandler.setNewThumbnail(this, aasId, contentType, filePath);
+ }
+
+ @Override
+ public void deleteThumbnail(String aasId) {
+ Resource thumbnail = getAssetInformation(aasId).getDefaultThumbnail();
+ AASThumbnailHandler.throwIfFileDoesNotExist(aasId, thumbnail);
+
+ deleteThumbnailFile(thumbnail);
+
+ updateThumbnailInAssetInformation(aasId);
+ }
+
private AasService getAasServiceOrThrow(String aasId) {
AssetAdministrationShell aas = aasBackend.findById(aasId).orElseThrow(() -> new ElementDoesNotExistException(aasId));
@@ -170,9 +212,30 @@ private void throwIfAasDoesNotExist(String aasId) {
throw new ElementDoesNotExistException(aasId);
}
- @Override
- public String getName() {
- return aasRepositoryName == null ? AasRepository.super.getName() : aasRepositoryName;
+ private void updateThumbnailInAssetInformation(String aasId) {
+ AssetInformation assetInfor = getAssetInformation(aasId);
+ assetInfor.getDefaultThumbnail().setContentType("");
+ assetInfor.getDefaultThumbnail().setPath("");
+ setAssetInformation(aasId, assetInfor);
+ }
+
+ private void deleteThumbnailFile(Resource thumbnail) {
+ String filePath = thumbnail.getPath();
+ java.io.File tmpFile = new java.io.File(filePath);
+ tmpFile.delete();
+ }
+
+ private void updateThumbnailFile(String aasId, String fileName, String contentType, InputStream inputStream, Resource thumbnail) {
+ String path = thumbnail.getPath();
+ AASThumbnailHandler.deleteExistingFile(path);
+ String filePath = createFile(aasId, fileName, inputStream);
+ AASThumbnailHandler.updateThumbnail(this, aasId, contentType, filePath);
+ }
+
+ private String createFile(String aasId, String fileName, InputStream inputStream) {
+ String filePath = AASThumbnailHandler.createFilePath(AASThumbnailHandler.getTemporaryDirectoryPath(), aasId, fileName);
+ AASThumbnailHandler.createFileAtSpecifiedPath(fileName, inputStream, filePath);
+ return filePath;
}
}
diff --git a/basyx.aasrepository/basyx.aasrepository-core/pom.xml b/basyx.aasrepository/basyx.aasrepository-core/pom.xml
index dd9335319..931042ba2 100644
--- a/basyx.aasrepository/basyx.aasrepository-core/pom.xml
+++ b/basyx.aasrepository/basyx.aasrepository-core/pom.xml
@@ -1,5 +1,6 @@
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
@@ -11,6 +12,11 @@
basyx.aasrepository-core
+
+ commons-io
+ commons-io
+ test
+
org.eclipse.digitaltwin.basyx
basyx.core
diff --git a/basyx.aasrepository/basyx.aasrepository-core/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/AasRepository.java b/basyx.aasrepository/basyx.aasrepository-core/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/AasRepository.java
index 3502f0f1b..2c423840c 100644
--- a/basyx.aasrepository/basyx.aasrepository-core/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/AasRepository.java
+++ b/basyx.aasrepository/basyx.aasrepository-core/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/AasRepository.java
@@ -24,6 +24,8 @@
******************************************************************************/
package org.eclipse.digitaltwin.basyx.aasrepository;
+import java.io.File;
+import java.io.InputStream;
import java.util.List;
import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
@@ -121,6 +123,37 @@ public interface AasRepository {
* @return the requested AAS
*/
public AssetInformation getAssetInformation(String aasId) throws ElementDoesNotExistException;
+
+ /**
+ * Get Thumbnail of the specific aas
+ *
+ * @param aasID
+ * the id of the AAS
+ * @return the file of the thumbnail
+ */
+ public File getThumbnail(String aasId);
+
+ /**
+ * Set Thumbnail of the AAS
+ *
+ * @param aasID
+ * the id of the AAS
+ * @param fileName
+ * name of the thumbnail file with extension
+ * @param contentType
+ * content type of the file
+ * @param inputStream
+ * inputstream of the thumbnail file
+ */
+ public void setThumbnail(String aasId, String fileName, String contentType, InputStream inputStream);
+
+ /**
+ * Delete the thumbnail file of the AAS
+ *
+ * @param aasId
+ * the id of the AAS
+ */
+ public void deleteThumbnail(String aasId);
/**
* Returns the name of the repository
diff --git a/basyx.aasrepository/basyx.aasrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/AasRepositorySuite.java b/basyx.aasrepository/basyx.aasrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/AasRepositorySuite.java
index 48e585cb6..b1f6e5fe8 100644
--- a/basyx.aasrepository/basyx.aasrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/AasRepositorySuite.java
+++ b/basyx.aasrepository/basyx.aasrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/AasRepositorySuite.java
@@ -29,11 +29,19 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import org.apache.commons.io.FileUtils;
import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
import org.eclipse.digitaltwin.aas4j.v3.model.AssetInformation;
import org.eclipse.digitaltwin.aas4j.v3.model.AssetKind;
@@ -46,11 +54,13 @@
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultReference;
import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException;
import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException;
import org.eclipse.digitaltwin.basyx.core.exceptions.IdentificationMismatchException;
import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult;
import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
import org.junit.Before;
import org.junit.Test;
+import org.springframework.core.io.ClassPathResource;
/**
* Testsuite for implementations of the AasRepository interface
@@ -70,6 +80,8 @@ public abstract class AasRepositorySuite {
private List preconfiguredShells = new ArrayList<>();
private static final String DUMMY_SUBMODEL_ID = "dummySubmodelId";
+ private static final String THUMBNAIL_FILE_PATH_1 = "BaSyx-Logo-1.png";
+ private static final String THUMBNAIL_FILE_PATH_2 = "BaSyx-Logo-2.png";
private AasRepository aasRepo;
@@ -80,15 +92,12 @@ public abstract class AasRepositorySuite {
@Before
public void createAasRepoWithDummyAas() {
aasRepo = getAasRepository();
-
+
sanitizeRepository();
- aas1 = new DefaultAssetAdministrationShell.Builder().id(AAS_1_ID)
- .submodels(createDummyReference(DUMMY_SUBMODEL_ID))
- .build();
+ aas1 = new DefaultAssetAdministrationShell.Builder().id(AAS_1_ID).submodels(createDummyReference(DUMMY_SUBMODEL_ID)).build();
- aas2 = new DefaultAssetAdministrationShell.Builder().id(AAS2)
- .build();
+ aas2 = new DefaultAssetAdministrationShell.Builder().id(AAS2).build();
AssetInformation assetInfo = createDummyAssetInformation();
aas2.setAssetInformation(assetInfo);
@@ -97,7 +106,7 @@ public void createAasRepoWithDummyAas() {
preconfiguredShells.forEach(shell -> aasRepo.createAas(shell));
}
-
+
@Test
public void getDefaultAasRepositoryName() {
assertEquals("aas-repo", aasRepo.getName());
@@ -106,8 +115,7 @@ public void getDefaultAasRepositoryName() {
@Test
public void allAasRetrieval() throws Exception {
PaginationInfo pInfo = new PaginationInfo(2, null);
- Collection coll = aasRepo.getAllAas(pInfo)
- .getResult();
+ Collection coll = aasRepo.getAllAas(pInfo).getResult();
assertEquals(preconfiguredShells, coll);
}
@@ -147,15 +155,13 @@ public void deleteNonExistingAas() {
public void getSubmodelReferences() {
Reference reference = createDummyReference(DUMMY_SUBMODEL_ID);
- List submodelReferences = aasRepo.getSubmodelReferences(aas1.getId(), noLimitPaginationInfo)
- .getResult();
+ List submodelReferences = aasRepo.getSubmodelReferences(aas1.getId(), noLimitPaginationInfo).getResult();
assertTrue(submodelReferences.contains(reference));
}
@Test(expected = ElementDoesNotExistException.class)
public void getSubmodelReferencesOfNonExistingAas() {
- aasRepo.getSubmodelReferences("doesNotMatter", noLimitPaginationInfo)
- .getResult();
+ aasRepo.getSubmodelReferences("doesNotMatter", noLimitPaginationInfo).getResult();
}
@Test
@@ -163,8 +169,7 @@ public void addSubmodelReference() {
Reference reference = createDummyReference(DUMMY_SUBMODEL_ID);
aasRepo.addSubmodelReference(aas1.getId(), reference);
- List submodelReferences = aasRepo.getSubmodelReferences(aas1.getId(), noLimitPaginationInfo)
- .getResult();
+ List submodelReferences = aasRepo.getSubmodelReferences(aas1.getId(), noLimitPaginationInfo).getResult();
assertTrue(submodelReferences.contains(reference));
}
@@ -180,8 +185,7 @@ public void removeSubmodelReference() {
Reference reference = createDummyReference(DUMMY_SUBMODEL_ID);
aasRepo.removeSubmodelReference(aas1.getId(), DUMMY_SUBMODEL_ID);
- List submodelReferences = aasRepo.getSubmodelReferences(aas1.getId(), noLimitPaginationInfo)
- .getResult();
+ List submodelReferences = aasRepo.getSubmodelReferences(aas1.getId(), noLimitPaginationInfo).getResult();
assertFalse(submodelReferences.contains(reference));
}
@@ -213,9 +217,7 @@ public void updateNonExistingAas() {
@Test(expected = IdentificationMismatchException.class)
public void updateExistingAasWithMismatchedIdentifier() {
- AssetAdministrationShell aas = new DefaultAssetAdministrationShell.Builder().id("mismatchId")
- .submodels(createDummyReference(DUMMY_SUBMODEL_ID))
- .build();
+ AssetAdministrationShell aas = new DefaultAssetAdministrationShell.Builder().id("mismatchId").submodels(createDummyReference(DUMMY_SUBMODEL_ID)).build();
aasRepo.updateAas(AAS_1_ID, aas);
}
@@ -247,10 +249,7 @@ public void getPaginatedAssetAdministrationShell() {
CursorResult> result = aasRepo.getAllAas(new PaginationInfo(1, null));
List resultList = result.getResult();
assertEquals(1, resultList.size());
- assertEquals(AAS_1_ID, resultList.stream()
- .findFirst()
- .get()
- .getId());
+ assertEquals(AAS_1_ID, resultList.stream().findFirst().get().getId());
}
@Test
@@ -261,43 +260,83 @@ public void getPaginatedAssetAdministrationShellIterating() {
result = aasRepo.getAllAas(new PaginationInfo(1, cursor));
List resultList = result.getResult();
assertEquals(1, resultList.size());
- assertEquals(AAS2, resultList.stream()
- .findFirst()
- .get()
- .getId());
+ assertEquals(AAS2, resultList.stream().findFirst().get().getId());
}
@Test
public void getPaginatedSubmodelReferencesPaginated() {
List submodelReferences = createDummyReferences();
- AssetAdministrationShell aas = new DefaultAssetAdministrationShell.Builder().id("paginatedAAS")
- .submodels(submodelReferences)
- .build();
+ AssetAdministrationShell aas = new DefaultAssetAdministrationShell.Builder().id("paginatedAAS").submodels(submodelReferences).build();
aasRepo.createAas(aas);
PaginationInfo pInfo = new PaginationInfo(1, "");
CursorResult> paginatedReferences = aasRepo.getSubmodelReferences("paginatedAAS", pInfo);
- assertEquals(1, paginatedReferences.getResult()
- .size());
- assertEquals(submodelReferences.stream()
- .findFirst()
- .get(),
- paginatedReferences.getResult()
- .stream()
- .findFirst()
- .get());
+ assertEquals(1, paginatedReferences.getResult().size());
+ assertEquals(submodelReferences.stream().findFirst().get(), paginatedReferences.getResult().stream().findFirst().get());
+ }
+
+ @Test
+ public void updateThumbnail() throws FileNotFoundException, IOException {
+ // Set the thumbnail of the AAS for the first time
+ aasRepo.setThumbnail(AAS2, THUMBNAIL_FILE_PATH_1, "", getInputStreamOfFileFromClasspath(THUMBNAIL_FILE_PATH_1));
+
+ // Set the thumbnail of the AAS for the second time with a new figure
+ aasRepo.setThumbnail(AAS2, THUMBNAIL_FILE_PATH_2, "", getInputStreamOfFileFromClasspath(THUMBNAIL_FILE_PATH_2));
+
+ File retrievedThumbnail = aasRepo.getThumbnail(AAS2);
+
+ assertEquals(readFile("src/test/resources/" + THUMBNAIL_FILE_PATH_2, Charset.defaultCharset()), new String(FileUtils.readFileToByteArray(retrievedThumbnail), Charset.defaultCharset()));
+
+ }
+
+ @Test
+ public void setThumbnail() throws FileNotFoundException, IOException {
+ aasRepo.setThumbnail(AAS2, THUMBNAIL_FILE_PATH_1, "", getInputStreamOfFileFromClasspath(THUMBNAIL_FILE_PATH_1));
+
+ File retrievedThumbnail = aasRepo.getThumbnail(AAS2);
+
+ assertEquals(readFile("src/test/resources/" + THUMBNAIL_FILE_PATH_1, Charset.defaultCharset()), new String(FileUtils.readFileToByteArray(retrievedThumbnail), Charset.defaultCharset()));
+
+ }
+
+ @Test
+ public void getThumbnail() throws IOException {
+ aasRepo.setThumbnail(AAS2, THUMBNAIL_FILE_PATH_1, "", getInputStreamOfFileFromClasspath(THUMBNAIL_FILE_PATH_1));
+
+ File retrievedThumbnail = aasRepo.getThumbnail(AAS2);
+
+ assertEquals(readFile("src/test/resources/" + THUMBNAIL_FILE_PATH_1, Charset.defaultCharset()), new String(FileUtils.readFileToByteArray(retrievedThumbnail), Charset.defaultCharset()));
+ }
+
+ @Test(expected = FileDoesNotExistException.class)
+ public void getNonExistingFile() {
+ aasRepo.getThumbnail(AAS2);
+ }
+
+ @Test(expected = FileDoesNotExistException.class)
+ public void deleteThumbnail() throws FileNotFoundException, IOException {
+ aasRepo.setThumbnail(AAS2, THUMBNAIL_FILE_PATH_1, "", getInputStreamOfFileFromClasspath(THUMBNAIL_FILE_PATH_1));
+ aasRepo.deleteThumbnail(AAS2);
+
+ aasRepo.getThumbnail(AAS2);
+ }
+
+ @Test(expected = FileDoesNotExistException.class)
+ public void deleteNonExistingFile() throws IOException {
+ aasRepo.deleteThumbnail(AAS2);
+ }
+
+ private static String readFile(String path, Charset encoding) throws IOException {
+ byte[] encoded = Files.readAllBytes(Paths.get(path));
+
+ return new String(encoded, encoding);
}
public static Reference createDummyReference(String submodelId) {
- return new DefaultReference.Builder().keys(new DefaultKey.Builder().type(KeyTypes.SUBMODEL)
- .value(submodelId)
- .build())
- .build();
+ return new DefaultReference.Builder().keys(new DefaultKey.Builder().type(KeyTypes.SUBMODEL).value(submodelId).build()).build();
}
private AssetInformation createDummyAssetInformation() {
- return new DefaultAssetInformation.Builder().assetKind(AssetKind.INSTANCE)
- .globalAssetId("assetIDTestKey")
- .build();
+ return new DefaultAssetInformation.Builder().assetKind(AssetKind.INSTANCE).globalAssetId("assetIDTestKey").build();
}
/**
@@ -306,13 +345,15 @@ private AssetInformation createDummyAssetInformation() {
private List createDummyReferences() {
List referenceList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
- Reference ref = new DefaultReference.Builder().type(ReferenceTypes.MODEL_REFERENCE)
- .keys(new DefaultKey.Builder().type(KeyTypes.SUBMODEL)
- .value("smRef_" + i)
- .build())
- .build();
+ Reference ref = new DefaultReference.Builder().type(ReferenceTypes.MODEL_REFERENCE).keys(new DefaultKey.Builder().type(KeyTypes.SUBMODEL).value("smRef_" + i).build()).build();
referenceList.add(ref);
}
return referenceList;
}
+
+ private InputStream getInputStreamOfFileFromClasspath(String fileName) throws FileNotFoundException, IOException {
+ ClassPathResource classPathResource = new ClassPathResource(fileName);
+
+ return classPathResource.getInputStream();
+ }
}
diff --git a/basyx.aasrepository/basyx.aasrepository-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/mqtt/MqttAasRepository.java b/basyx.aasrepository/basyx.aasrepository-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/mqtt/MqttAasRepository.java
index e73e6b733..f2d3c90e6 100644
--- a/basyx.aasrepository/basyx.aasrepository-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/mqtt/MqttAasRepository.java
+++ b/basyx.aasrepository/basyx.aasrepository-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/mqtt/MqttAasRepository.java
@@ -24,6 +24,8 @@
******************************************************************************/
package org.eclipse.digitaltwin.basyx.aasrepository.feature.mqtt;
+import java.io.File;
+import java.io.InputStream;
import java.util.List;
import org.eclipse.digitaltwin.aas4j.v3.dataformat.SerializationException;
@@ -172,4 +174,19 @@ private MqttMessage createMqttMessage(String payload) {
}
}
+ @Override
+ public File getThumbnail(String aasId) {
+ return decorated.getThumbnail(aasId);
+ }
+
+ @Override
+ public void setThumbnail(String aasId, String fileName, String contentType, InputStream inputStream) {
+ decorated.setThumbnail(aasId, fileName, contentType, inputStream);
+ }
+
+ @Override
+ public void deleteThumbnail(String aasId) {
+ decorated.deleteThumbnail(aasId);
+ }
+
}
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
index 18c73a895..021daaf4c 100644
--- 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
@@ -24,6 +24,8 @@
******************************************************************************/
package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration;
+import java.io.File;
+import java.io.InputStream;
import java.util.List;
import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
@@ -168,4 +170,19 @@ private boolean shellExistsOnRegistry(String shellId, RegistryAndDiscoveryInterf
}
}
+ @Override
+ public File getThumbnail(String aasId) {
+ return decorated.getThumbnail(aasId);
+ }
+
+ @Override
+ public void setThumbnail(String aasId, String fileName, String contentType, InputStream inputStream) {
+ decorated.setThumbnail(aasId, fileName, contentType, inputStream);
+ }
+
+ @Override
+ public void deleteThumbnail(String aasId) {
+ decorated.deleteThumbnail(aasId);
+ }
+
}
diff --git a/basyx.aasrepository/basyx.aasrepository-http/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/http/AasRepositoryApiHTTPController.java b/basyx.aasrepository/basyx.aasrepository-http/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/http/AasRepositoryApiHTTPController.java
index 7268cab43..1436e0366 100644
--- a/basyx.aasrepository/basyx.aasrepository-http/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/http/AasRepositoryApiHTTPController.java
+++ b/basyx.aasrepository/basyx.aasrepository-http/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/http/AasRepositoryApiHTTPController.java
@@ -25,6 +25,10 @@
package org.eclipse.digitaltwin.basyx.aasrepository.http;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -45,11 +49,14 @@
import org.eclipse.digitaltwin.basyx.http.pagination.PagedResult;
import org.eclipse.digitaltwin.basyx.http.pagination.PagedResultPagingMetadata;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
@jakarta.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.SpringCodegen", date = "2022-01-10T15:59:05.892Z[GMT]")
@RestController
@@ -168,4 +175,46 @@ private String getEncodedCursorFromCursorResult(CursorResult> cursorResult) {
return Base64UrlEncodedCursor.encodeCursor(cursorResult.getCursor());
}
+
+ @Override
+ public ResponseEntity deleteThumbnailAasRepository(Base64UrlEncodedIdentifier aasIdentifier) {
+ aasRepository.deleteThumbnail(aasIdentifier.getIdentifier());
+ return new ResponseEntity(HttpStatus.OK);
+ }
+
+ @Override
+ public ResponseEntity getThumbnailAasRepository(Base64UrlEncodedIdentifier aasIdentifier) {
+ try {
+ FileInputStream fileInputStream = new FileInputStream(aasRepository.getThumbnail(aasIdentifier.getIdentifier()));
+ Resource resource = new InputStreamResource(fileInputStream);
+ return new ResponseEntity(resource, HttpStatus.OK);
+ } catch (FileNotFoundException e) {
+ return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @Override
+ public ResponseEntity putThumbnailAasRepository(Base64UrlEncodedIdentifier aasIdentifier, String fileName, @Valid MultipartFile file) {
+ InputStream fileInputstream = null;
+ try {
+ fileInputstream = file.getInputStream();
+ aasRepository.setThumbnail(aasIdentifier.getIdentifier(), fileName, file.getContentType(), fileInputstream);
+ closeInputStream(fileInputstream);
+ return new ResponseEntity(HttpStatus.OK);
+ } catch (IOException e) {
+ closeInputStream(fileInputstream);
+ return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ private void closeInputStream(InputStream fileInputstream) {
+ if (fileInputstream == null)
+ return;
+
+ try {
+ fileInputstream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
}
diff --git a/basyx.aasrepository/basyx.aasrepository-http/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/http/AasRepositoryHTTPApi.java b/basyx.aasrepository/basyx.aasrepository-http/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/http/AasRepositoryHTTPApi.java
index 8ed0b9616..dd138be5c 100644
--- a/basyx.aasrepository/basyx.aasrepository-http/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/http/AasRepositoryHTTPApi.java
+++ b/basyx.aasrepository/basyx.aasrepository-http/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/http/AasRepositoryHTTPApi.java
@@ -51,6 +51,7 @@
import org.eclipse.digitaltwin.basyx.http.model.Result;
import org.eclipse.digitaltwin.basyx.http.pagination.Base64UrlEncodedCursor;
import org.eclipse.digitaltwin.basyx.http.pagination.PagedResult;
+import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
@@ -58,6 +59,8 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.multipart.MultipartFile;
@jakarta.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.SpringCodegen", date = "2022-01-10T15:59:05.892Z[GMT]")
@Validated
@@ -254,4 +257,61 @@ ResponseEntity putAssetInformationAasRepository(
@RequestMapping(value = "/shells/{aasIdentifier}/asset-information", produces = { "application/json" }, method = RequestMethod.GET)
ResponseEntity getAssetInformationAasRepository(
@Parameter(in = ParameterIn.PATH, description = "The Asset Administration Shell’s unique id (UTF8-BASE64-URL-encoded)", required = true, schema = @Schema()) @PathVariable("aasIdentifier") Base64UrlEncodedIdentifier aasIdentifier);
+
+ @Operation(summary = "", description = "", tags = { "Asset Administration Shell Repository API" })
+ @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Thumbnail deletion successful"),
+
+ @ApiResponse(responseCode = "400", description = "Bad Request, e.g. the request parameters of the format of the request body is wrong.", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "401", description = "Unauthorized, e.g. the server refused the authorization attempt.", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "200", description = "Default error handling for unmentioned status codes", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))) })
+ @RequestMapping(value = "/shells/{aasIdentifier}/asset-information/thumbnail", produces = { "application/json" }, method = RequestMethod.DELETE)
+ ResponseEntity deleteThumbnailAasRepository(
+ @Parameter(in = ParameterIn.PATH, description = "The Asset Administration Shell’s unique id (UTF8-BASE64-URL-encoded)", required = true, schema = @Schema()) @PathVariable("aasIdentifier") Base64UrlEncodedIdentifier aasIdentifier);
+
+ @Operation(summary = "", description = "", tags = { "Asset Administration Shell Repository API" })
+ @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "The thumbnail of the Asset Information.", content = @Content(mediaType = "application/octet-stream", schema = @Schema(implementation = Resource.class))),
+
+ @ApiResponse(responseCode = "400", description = "Bad Request, e.g. the request parameters of the format of the request body is wrong.", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "401", description = "Unauthorized, e.g. the server refused the authorization attempt.", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "200", description = "Default error handling for unmentioned status codes", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))) })
+ @RequestMapping(value = "/shells/{aasIdentifier}/asset-information/thumbnail", produces = { "application/octet-stream", "application/json" }, method = RequestMethod.GET)
+ ResponseEntity getThumbnailAasRepository(
+ @Parameter(in = ParameterIn.PATH, description = "The Asset Administration Shell’s unique id (UTF8-BASE64-URL-encoded)", required = true, schema = @Schema()) @PathVariable("aasIdentifier") Base64UrlEncodedIdentifier aasIdentifier);
+
+ @Operation(summary = "", description = "", tags = { "Asset Administration Shell Repository API" })
+ @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "Thumbnail updated successfully"),
+
+ @ApiResponse(responseCode = "400", description = "Bad Request, e.g. the request parameters of the format of the request body is wrong.", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "401", description = "Unauthorized, e.g. the server refused the authorization attempt.", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))),
+
+ @ApiResponse(responseCode = "200", description = "Default error handling for unmentioned status codes", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))) })
+ @RequestMapping(value = "/shells/{aasIdentifier}/asset-information/thumbnail", produces = { "application/json" }, consumes = { "multipart/form-data" }, method = RequestMethod.PUT)
+ ResponseEntity putThumbnailAasRepository(
+ @Parameter(in = ParameterIn.PATH, description = "The Asset Administration Shell’s unique id (UTF8-BASE64-URL-encoded)", required = true, schema = @Schema()) @PathVariable("aasIdentifier") Base64UrlEncodedIdentifier aasIdentifier,
+ @Parameter(in = ParameterIn.DEFAULT, description = "", required = true, schema = @Schema()) @RequestParam(value = "fileName", required = true) String fileName,
+ @Parameter(description = "file detail") @Valid @RequestPart("file") MultipartFile file);
+
}
diff --git a/basyx.aasrepository/basyx.aasrepository-http/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/http/AasRepositoryHTTPSuite.java b/basyx.aasrepository/basyx.aasrepository-http/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/http/AasRepositoryHTTPSuite.java
index 23e5ce995..c91050f39 100644
--- a/basyx.aasrepository/basyx.aasrepository-http/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/http/AasRepositoryHTTPSuite.java
+++ b/basyx.aasrepository/basyx.aasrepository-http/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/http/AasRepositoryHTTPSuite.java
@@ -26,13 +26,21 @@
package org.eclipse.digitaltwin.basyx.aasrepository.http;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import org.apache.hc.client5.http.ClientProtocolException;
+import org.apache.hc.client5.http.classic.methods.HttpPut;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ParseException;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.eclipse.digitaltwin.aas4j.v3.dataformat.DeserializationException;
import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier;
import org.eclipse.digitaltwin.basyx.http.pagination.Base64UrlEncodedCursor;
@@ -40,7 +48,9 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpStatus;
+import org.springframework.util.ResourceUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
@@ -53,6 +63,7 @@
*/
public abstract class AasRepositoryHTTPSuite {
private static final String dummyAasId = "customIdentifier";
+ private static final String THUMBNAIL_FILE_PATH = "BaSyx-Logo.png";
private final String CURSOR = "AasNumber3Identifier";
private final String ENCODED_CURSOR = Base64UrlEncodedCursor.encodeCursor(CURSOR);
@@ -283,6 +294,64 @@ public void paginationResult() throws FileNotFoundException, IOException, ParseE
BaSyxHttpTestUtils.assertSameJSONContent(getPaginatedAas1JSONString(), getJSONWithoutCursorInfo(response));
}
+ @Test
+ public void uploadThumbnailToShell() throws IOException {
+ createDummyAasOnServer(getAas1JSONString());
+ CloseableHttpResponse getThumbnailResponse = uploadThumbnail(dummyAasId);
+
+ assertEquals(HttpStatus.OK.value(), getThumbnailResponse.getCode());
+
+ getThumbnailResponse.close();
+ }
+
+ @Test
+ public void getThumbnail() throws FileNotFoundException, IOException, ParseException {
+ createDummyAasOnServer(getAas1JSONString());
+
+ byte[] expectedFile = readBytesFromClasspath(THUMBNAIL_FILE_PATH);
+
+ uploadThumbnail(dummyAasId);
+
+ CloseableHttpResponse response = BaSyxHttpTestUtils.executeGetOnURL(BaSyxHttpTestUtils.getThumbnailAccessURL(getURL(), dummyAasId));
+ assertEquals(HttpStatus.OK.value(), response.getCode());
+
+ byte[] actualFile = EntityUtils.toByteArray(response.getEntity());
+
+ response.close();
+
+ assertArrayEquals(expectedFile, actualFile);
+ }
+
+ @Test
+ public void getFileFromNotExistElement() throws FileNotFoundException, UnsupportedEncodingException, ClientProtocolException, IOException {
+ CloseableHttpResponse response = BaSyxHttpTestUtils.executeGetOnURL(BaSyxHttpTestUtils.getThumbnailAccessURL(getURL(), dummyAasId));
+
+ assertEquals(HttpStatus.NOT_FOUND.value(), response.getCode());
+
+ response.close();
+ }
+
+ @Test
+ public void deleteThumbnail() throws FileNotFoundException, IOException {
+ createDummyAasOnServer(getAas1JSONString());
+ uploadThumbnail(dummyAasId);
+
+ CloseableHttpResponse response = BaSyxHttpTestUtils.executeDeleteOnURL(BaSyxHttpTestUtils.getThumbnailAccessURL(getURL(), dummyAasId));
+ assertEquals(HttpStatus.OK.value(), response.getCode());
+
+ response = BaSyxHttpTestUtils.executeGetOnURL(BaSyxHttpTestUtils.getThumbnailAccessURL(getURL(), dummyAasId));
+ assertEquals(HttpStatus.NOT_FOUND.value(), response.getCode());
+
+ response.close();
+ }
+
+ @Test
+ public void deleteNonExistingThumbnail() throws FileNotFoundException, UnsupportedEncodingException, ClientProtocolException, IOException {
+ CloseableHttpResponse response = BaSyxHttpTestUtils.executeDeleteOnURL(BaSyxHttpTestUtils.getThumbnailAccessURL(getURL(), dummyAasId));
+
+ assertEquals(HttpStatus.NOT_FOUND.value(), response.getCode());
+ }
+
private String getPaginatedAas1JSONString() throws FileNotFoundException, IOException {
return BaSyxHttpTestUtils.readJSONStringFromClasspath("PaginatedAasSimple_1.json");
}
@@ -333,6 +402,8 @@ protected String getSpecificAasAccessURL(String aasId) {
return getURL() + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(aasId);
}
+
+
private void assertAasIsNotOnServer(String aasId) throws IOException {
CloseableHttpResponse getResponse = getSpecificAas(aasId);
assertEquals(HttpStatus.NOT_FOUND.value(), getResponse.getCode());
@@ -387,4 +458,24 @@ private String getPaginatedSingleSMReferenceJson() throws FileNotFoundException,
return BaSyxHttpTestUtils.readJSONStringFromClasspath("PaginatedSingleSMReference.json");
}
+ private CloseableHttpResponse uploadThumbnail(String aasId) throws IOException {
+ CloseableHttpClient client = HttpClients.createDefault();
+
+ java.io.File file = ResourceUtils.getFile("src/test/resources/" + THUMBNAIL_FILE_PATH);
+
+ HttpPut putRequest = BaSyxHttpTestUtils.createPutRequestWithFile(getURL(), aasId, THUMBNAIL_FILE_PATH, file);
+
+ return BaSyxHttpTestUtils.executePutRequest(client, putRequest);
+ }
+
+
+
+ private byte[] readBytesFromClasspath(String fileName) throws FileNotFoundException, IOException {
+ ClassPathResource classPathResource = new ClassPathResource(fileName);
+ InputStream in = classPathResource.getInputStream();
+
+ return in.readAllBytes();
+ }
+
+
}
diff --git a/basyx.aasrepository/basyx.aasrepository-http/src/test/resources/BaSyx-Logo.png b/basyx.aasrepository/basyx.aasrepository-http/src/test/resources/BaSyx-Logo.png
new file mode 100644
index 000000000..da613e94c
Binary files /dev/null and b/basyx.aasrepository/basyx.aasrepository-http/src/test/resources/BaSyx-Logo.png differ
diff --git a/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/FileDoesNotExistException.java b/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/FileDoesNotExistException.java
index bc1ae701f..f47529d9f 100644
--- a/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/FileDoesNotExistException.java
+++ b/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/FileDoesNotExistException.java
@@ -41,7 +41,7 @@ public FileDoesNotExistException(String elementId) {
}
private static String getMsg(String elementId) {
- return "Requested File inside File SubmodelElement with ID : " + elementId + " does not exist";
+ return "Requested File inside the Asset administration shell or File SubmodelElement with ID : " + elementId + " does not exist";
}
}
diff --git a/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/serialization/BaSyxHttpTestUtils.java b/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/serialization/BaSyxHttpTestUtils.java
index bd54c3db6..94b7730b6 100644
--- a/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/serialization/BaSyxHttpTestUtils.java
+++ b/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/serialization/BaSyxHttpTestUtils.java
@@ -38,12 +38,17 @@
import org.apache.hc.client5.http.classic.methods.HttpPatch;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpPut;
+import org.apache.hc.client5.http.entity.mime.FileBody;
+import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier;
import org.springframework.core.io.ClassPathResource;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -198,6 +203,33 @@ public static String removeCursorFromJSON(String inputJSON) throws JsonMappingEx
return mapper.writeValueAsString(rootNode);
}
+ public static CloseableHttpResponse executePutRequest(CloseableHttpClient client, HttpPut putRequest) throws IOException {
+ CloseableHttpResponse response = client.execute(putRequest);
+
+ HttpEntity responseEntity = response.getEntity();
+
+ EntityUtils.consume(responseEntity);
+ return response;
+ }
+
+ public static HttpPut createPutRequestWithFile(String url, String aasId, String fileName, java.io.File file) {
+ HttpPut putRequest = new HttpPut(getThumbnailAccessURL(url, aasId));
+
+ MultipartEntityBuilder builder = MultipartEntityBuilder.create();
+
+ builder.addPart("file", new FileBody(file));
+ builder.addTextBody("fileName", fileName);
+ builder.setContentType(ContentType.MULTIPART_FORM_DATA);
+
+ HttpEntity multipart = builder.build();
+ putRequest.setEntity(multipart);
+ return putRequest;
+ }
+
+ public static String getThumbnailAccessURL(String url, String aasId) {
+ return url + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(aasId) + "/asset-information/thumbnail";
+ }
+
private static HttpPatch createPatchRequestWithHeader(String url, String content) {
HttpPatch patchRequest = new HttpPatch(url);