diff --git a/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/AasEnvironmentLoaderTest.java b/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/AasEnvironmentLoaderTest.java
index dc7e8634a..02bebe59d 100644
--- a/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/AasEnvironmentLoaderTest.java
+++ b/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/AasEnvironmentLoaderTest.java
@@ -41,6 +41,7 @@
import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionInMemoryBackendProvider;
import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionRepository;
import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException;
+import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository;
import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelInMemoryBackendProvider;
import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository;
@@ -77,7 +78,7 @@ public class AasEnvironmentLoaderTest {
@Before
public void setUp() {
- submodelRepository = Mockito.spy(new CrudSubmodelRepository(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory()));
+ submodelRepository = Mockito.spy(new CrudSubmodelRepository(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory(new InMemoryFileRepository())));
aasRepository = Mockito.spy(new CrudAasRepository(new AasInMemoryBackendProvider(), new InMemoryAasServiceFactory()));
conceptDescriptionRepository = Mockito.spy(new CrudConceptDescriptionRepository(new ConceptDescriptionInMemoryBackendProvider()));
}
diff --git a/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/TestAASEnvironmentSerialization.java b/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/TestAASEnvironmentSerialization.java
index 9f5379d5f..b798a176c 100644
--- a/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/TestAASEnvironmentSerialization.java
+++ b/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/TestAASEnvironmentSerialization.java
@@ -35,6 +35,7 @@
import org.eclipse.digitaltwin.basyx.aasservice.backend.InMemoryAasServiceFactory;
import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionInMemoryBackendProvider;
import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionRepository;
+import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository;
import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelInMemoryBackendProvider;
import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository;
@@ -62,7 +63,7 @@ public class TestAASEnvironmentSerialization {
@Before
public void setup() {
- submodelRepository = new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory()).create();
+ submodelRepository = new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory(new InMemoryFileRepository())).create();
aasRepository = new SimpleAasRepositoryFactory(new AasInMemoryBackendProvider(), new InMemoryAasServiceFactory()).create();
conceptDescriptionRepository = new SimpleConceptDescriptionRepositoryFactory(new ConceptDescriptionInMemoryBackendProvider(), createDummyConceptDescriptions(), "cdRepo").create();
diff --git a/basyx.common/basyx.filerepository-backend-inmemory/pom.xml b/basyx.common/basyx.filerepository-backend-inmemory/pom.xml
new file mode 100644
index 000000000..a6f8cd770
--- /dev/null
+++ b/basyx.common/basyx.filerepository-backend-inmemory/pom.xml
@@ -0,0 +1,25 @@
+
+ 4.0.0
+
+ org.eclipse.digitaltwin.basyx
+ basyx.common
+ ${revision}
+
+ basyx.filerepository-backend-inmemory
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.core
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.filerepository-backend
+
+
+ commons-io
+ commons-io
+
+
+
\ No newline at end of file
diff --git a/basyx.submodelrepository/basyx.submodelrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/InMemorySubmodelFileRepository.java b/basyx.common/basyx.filerepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/core/filerepository/InMemoryFileRepository.java
similarity index 91%
rename from basyx.submodelrepository/basyx.submodelrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/InMemorySubmodelFileRepository.java
rename to basyx.common/basyx.filerepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/core/filerepository/InMemoryFileRepository.java
index 386be6f9d..be4ce3b90 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/InMemorySubmodelFileRepository.java
+++ b/basyx.common/basyx.filerepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/core/filerepository/InMemoryFileRepository.java
@@ -23,7 +23,7 @@
* SPDX-License-Identifier: MIT
******************************************************************************/
-package org.eclipse.digitaltwin.basyx.submodelrepository;
+package org.eclipse.digitaltwin.basyx.core.filerepository;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -37,15 +37,17 @@
import org.apache.commons.io.IOUtils;
import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException;
import org.eclipse.digitaltwin.basyx.core.exceptions.FileHandlingException;
-import org.eclipse.digitaltwin.basyx.core.file.FileMetadata;
-import org.eclipse.digitaltwin.basyx.core.file.FileRepository;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.stereotype.Component;
/**
* An InMemory implementation of the {@link FileRepository}
*
* @author danish
*/
-public class InMemorySubmodelFileRepository implements FileRepository {
+@Component
+@ConditionalOnExpression("'${basyx.backend}'.equals('InMemory')")
+public class InMemoryFileRepository implements FileRepository {
private static final String TEMP_DIRECTORY_PREFIX = "basyx-temp";
private String tmpDirectory = getTemporaryDirectoryPath();
@@ -53,7 +55,7 @@ public class InMemorySubmodelFileRepository implements FileRepository {
@Override
public String save(FileMetadata fileMetadata) throws FileHandlingException {
String filePath = createFilePath(fileMetadata.getFileName());
-
+
java.io.File targetFile = new java.io.File(filePath);
try (FileOutputStream outStream = new FileOutputStream(targetFile)) {
@@ -61,7 +63,7 @@ public String save(FileMetadata fileMetadata) throws FileHandlingException {
} catch (IOException e) {
throw new FileHandlingException(fileMetadata.getFileName());
}
-
+
fileMetadata.setFileName(filePath);
return filePath;
@@ -69,7 +71,7 @@ public String save(FileMetadata fileMetadata) throws FileHandlingException {
@Override
public InputStream find(String fileId) throws FileDoesNotExistException {
-
+
try {
return new FileInputStream(fileId);
} catch (FileNotFoundException e) {
@@ -79,10 +81,10 @@ public InputStream find(String fileId) throws FileDoesNotExistException {
@Override
public void delete(String fileId) throws FileDoesNotExistException {
-
+
if (!exists(fileId))
throw new FileDoesNotExistException();
-
+
java.io.File tmpFile = new java.io.File(fileId);
tmpFile.delete();
@@ -90,36 +92,36 @@ public void delete(String fileId) throws FileDoesNotExistException {
@Override
public boolean exists(String fileId) {
-
+
if (fileId.isBlank() || !isFilePathValid(fileId))
return false;
-
+
return Files.exists(Paths.get(fileId));
}
-
+
private boolean isFilePathValid(String filePath) {
-
+
try {
Paths.get(filePath);
} catch (InvalidPathException | NullPointerException ex) {
return false;
}
-
+
return true;
}
private String getTemporaryDirectoryPath() {
String tempDirectoryPath = "";
-
+
try {
tempDirectoryPath = Files.createTempDirectory(TEMP_DIRECTORY_PREFIX).toAbsolutePath().toString();
} catch (IOException e) {
throw new RuntimeException(String.format("Unable to create the temporary directory with prefix: %s", TEMP_DIRECTORY_PREFIX));
}
-
+
return tempDirectoryPath;
}
-
+
private String createFilePath(String fileName) {
return tmpDirectory + "/" + fileName;
}
diff --git a/basyx.common/basyx.filerepository-backend-mongodb/pom.xml b/basyx.common/basyx.filerepository-backend-mongodb/pom.xml
new file mode 100644
index 000000000..1d160db84
--- /dev/null
+++ b/basyx.common/basyx.filerepository-backend-mongodb/pom.xml
@@ -0,0 +1,21 @@
+
+ 4.0.0
+
+ org.eclipse.digitaltwin.basyx
+ basyx.common
+ ${revision}
+
+ basyx.filerepository-backend-mongodb
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.filerepository-backend
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.mongodbcore
+
+
+
\ No newline at end of file
diff --git a/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/MongoDBSubmodelFileRepository.java b/basyx.common/basyx.filerepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/core/filerepository/MongoDBFileRepository.java
similarity index 84%
rename from basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/MongoDBSubmodelFileRepository.java
rename to basyx.common/basyx.filerepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/core/filerepository/MongoDBFileRepository.java
index aa427294f..aa56b43e3 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/MongoDBSubmodelFileRepository.java
+++ b/basyx.common/basyx.filerepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/core/filerepository/MongoDBFileRepository.java
@@ -23,7 +23,7 @@
* SPDX-License-Identifier: MIT
******************************************************************************/
-package org.eclipse.digitaltwin.basyx.submodelrepository;
+package org.eclipse.digitaltwin.basyx.core.filerepository;
import java.io.IOException;
import java.io.InputStream;
@@ -34,21 +34,24 @@
import org.bson.types.ObjectId;
import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException;
import org.eclipse.digitaltwin.basyx.core.exceptions.FileHandlingException;
-import org.eclipse.digitaltwin.basyx.core.file.FileMetadata;
-import org.eclipse.digitaltwin.basyx.core.file.FileRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
+import org.springframework.stereotype.Component;
import com.mongodb.client.gridfs.model.GridFSFile;
-import org.springframework.data.mongodb.core.query.Query;
-
/**
* A MongoDB implementation of the {@link FileRepository}
*
* @author danish
*/
-public class MongoDBSubmodelFileRepository implements FileRepository {
+@Component
+@ConditionalOnExpression("'${basyx.backend}'.equals('MongoDB')")
+public class MongoDBFileRepository implements FileRepository {
private static final String MONGO_ID = "_id";
private static final String GRIDFS_ID_DELIMITER = "#";
@@ -56,10 +59,19 @@ public class MongoDBSubmodelFileRepository implements FileRepository {
private GridFsTemplate gridFsTemplate;
- public MongoDBSubmodelFileRepository(GridFsTemplate gridFsTemplate) {
+ @Autowired
+ public MongoDBFileRepository(MongoTemplate mongoTemplate) {
+ this(buildDefaultGridFsTemplate(mongoTemplate));
+ }
+
+ public MongoDBFileRepository(GridFsTemplate gridFsTemplate) {
this.gridFsTemplate = gridFsTemplate;
}
+ public static GridFsTemplate buildDefaultGridFsTemplate(MongoTemplate mongoTemplate) {
+ return new GridFsTemplate(mongoTemplate.getMongoDatabaseFactory(), mongoTemplate.getConverter());
+ }
+
@Override
public String save(FileMetadata fileMetadata) throws FileHandlingException {
ObjectId id = gridFsTemplate.store(fileMetadata.getFileContent(), fileMetadata.getFileName(), fileMetadata.getContentType());
@@ -76,7 +88,7 @@ public InputStream find(String fileId) throws FileDoesNotExistException {
if (!exists(fileId))
throw new FileDoesNotExistException();
-
+
String mongoDBfileId = getFileId(fileId);
GridFSFile file = getFile(mongoDBfileId);
@@ -89,7 +101,7 @@ public void delete(String fileId) throws FileDoesNotExistException {
if (!exists(fileId))
throw new FileDoesNotExistException();
-
+
String mongoDBfileId = getFileId(fileId);
gridFsTemplate.delete(new Query(Criteria.where(MONGO_ID).is(mongoDBfileId)));
@@ -97,9 +109,9 @@ public void delete(String fileId) throws FileDoesNotExistException {
@Override
public boolean exists(String fileId) {
-
+
String mongoDBfileId = getFileId(fileId);
-
+
if (mongoDBfileId.isBlank())
return false;
@@ -107,20 +119,20 @@ public boolean exists(String fileId) {
return gridFSFile != null;
}
-
+
private String getFileId(String value) {
-
+
if (value.isBlank())
return "";
-
+
String fileName = Paths.get(value).getFileName().toString();
-
+
try {
return fileName.substring(0, fileName.indexOf(GRIDFS_ID_DELIMITER));
} catch (IndexOutOfBoundsException e) {
return "";
}
-
+
}
private GridFSFile getFile(String mongoDBfileId) {
@@ -138,22 +150,22 @@ private InputStream getGridFsFileAsInputStream(GridFSFile file) {
}
private String createFilePath(String id, String fileName) {
-
+
Path tempDir = createTempDirectory(TEMP_DIR_PREFIX);
String temporaryDirectoryPath = tempDir.toAbsolutePath().toString();
return temporaryDirectoryPath + "/" + id + GRIDFS_ID_DELIMITER + fileName;
}
-
+
private Path createTempDirectory(String prefix) {
-
+
try {
return Files.createTempDirectory(prefix);
} catch (IOException e) {
throw new FileHandlingException("Exception occurred while creating temporary directory with prefix '" + TEMP_DIR_PREFIX + "'." + e.getMessage());
}
-
+
}
}
diff --git a/basyx.common/basyx.filerepository-backend/pom.xml b/basyx.common/basyx.filerepository-backend/pom.xml
new file mode 100644
index 000000000..db8a3a238
--- /dev/null
+++ b/basyx.common/basyx.filerepository-backend/pom.xml
@@ -0,0 +1,16 @@
+
+ 4.0.0
+
+ org.eclipse.digitaltwin.basyx
+ basyx.common
+ ${revision}
+
+ basyx.filerepository-backend
+ BaSyx File Repository Backend
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.core
+
+
+
\ No newline at end of file
diff --git a/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/file/FileMetadata.java b/basyx.common/basyx.filerepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/core/filerepository/FileMetadata.java
similarity index 97%
rename from basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/file/FileMetadata.java
rename to basyx.common/basyx.filerepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/core/filerepository/FileMetadata.java
index 0ada012f2..4f85eccfd 100644
--- a/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/file/FileMetadata.java
+++ b/basyx.common/basyx.filerepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/core/filerepository/FileMetadata.java
@@ -23,7 +23,7 @@
* SPDX-License-Identifier: MIT
******************************************************************************/
-package org.eclipse.digitaltwin.basyx.core.file;
+package org.eclipse.digitaltwin.basyx.core.filerepository;
import java.io.InputStream;
diff --git a/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/file/FileRepository.java b/basyx.common/basyx.filerepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/core/filerepository/FileRepository.java
similarity index 97%
rename from basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/file/FileRepository.java
rename to basyx.common/basyx.filerepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/core/filerepository/FileRepository.java
index e123ada2a..95b9b40cb 100644
--- a/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/file/FileRepository.java
+++ b/basyx.common/basyx.filerepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/core/filerepository/FileRepository.java
@@ -23,7 +23,7 @@
* SPDX-License-Identifier: MIT
******************************************************************************/
-package org.eclipse.digitaltwin.basyx.core.file;
+package org.eclipse.digitaltwin.basyx.core.filerepository;
import java.io.InputStream;
diff --git a/basyx.common/pom.xml b/basyx.common/pom.xml
index 54730cd6f..16b9c06f6 100644
--- a/basyx.common/pom.xml
+++ b/basyx.common/pom.xml
@@ -20,5 +20,8 @@
basyx.mongocore
basyx.authorization
basyx.client
+ basyx.filerepository-backend
+ basyx.filerepository-backend-inmemory
+ basyx.filerepository-backend-mongodb
\ No newline at end of file
diff --git a/basyx.submodelrepository/basyx.submodelrepository-backend-inmemory/pom.xml b/basyx.submodelrepository/basyx.submodelrepository-backend-inmemory/pom.xml
index 982c03d6c..11d97c975 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-backend-inmemory/pom.xml
+++ b/basyx.submodelrepository/basyx.submodelrepository-backend-inmemory/pom.xml
@@ -55,5 +55,11 @@
commons-io
commons-io
+
+ org.eclipse.digitaltwin.basyx
+
+ basyx.filerepository-backend-inmemory
+
+
\ No newline at end of file
diff --git a/basyx.submodelrepository/basyx.submodelrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/SubmodelInMemoryBackendProvider.java b/basyx.submodelrepository/basyx.submodelrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/SubmodelInMemoryBackendProvider.java
index 953ae24c9..79b69eb2a 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/SubmodelInMemoryBackendProvider.java
+++ b/basyx.submodelrepository/basyx.submodelrepository-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/SubmodelInMemoryBackendProvider.java
@@ -26,7 +26,6 @@
package org.eclipse.digitaltwin.basyx.submodelrepository;
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
-import org.eclipse.digitaltwin.basyx.core.file.FileRepository;
import org.eclipse.digitaltwin.basyx.submodelrepository.backend.SubmodelBackendProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.data.repository.CrudRepository;
@@ -47,9 +46,4 @@ public CrudRepository getCrudRepository() {
return new SubmodelInMemoryBackend();
}
- @Override
- public FileRepository getFileRepository() {
- return new InMemorySubmodelFileRepository();
- }
-
}
diff --git a/basyx.submodelrepository/basyx.submodelrepository-backend-inmemory/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/TestInMemorySubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-backend-inmemory/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/TestInMemorySubmodelRepository.java
index 3ab27ce78..53e7c15bb 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-backend-inmemory/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/TestInMemorySubmodelRepository.java
+++ b/basyx.submodelrepository/basyx.submodelrepository-backend-inmemory/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/TestInMemorySubmodelRepository.java
@@ -31,6 +31,7 @@
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException;
+import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository;
import org.eclipse.digitaltwin.basyx.submodelrepository.backend.CrudSubmodelRepository;
import org.eclipse.digitaltwin.basyx.submodelrepository.backend.SimpleSubmodelRepositoryFactory;
import org.eclipse.digitaltwin.basyx.submodelrepository.core.SubmodelRepositorySuite;
@@ -53,12 +54,12 @@ public class TestInMemorySubmodelRepository extends SubmodelRepositorySuite {
@Override
protected SubmodelRepository getSubmodelRepository() {
- return new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory()).create();
+ return new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory(new InMemoryFileRepository())).create();
}
@Override
protected SubmodelRepository getSubmodelRepository(Collection submodels) {
- return new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory(), submodels).create();
+ return new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory(new InMemoryFileRepository()), submodels).create();
}
@Override
@@ -70,7 +71,7 @@ protected boolean fileExistsInStorage(String fileValue) {
@Test
public void getConfiguredInMemorySmRepositoryName() {
- SubmodelRepository repo = new CrudSubmodelRepository(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory(), CONFIGURED_SM_REPO_NAME);
+ SubmodelRepository repo = new CrudSubmodelRepository(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory(new InMemoryFileRepository()), CONFIGURED_SM_REPO_NAME);
assertEquals(CONFIGURED_SM_REPO_NAME, repo.getName());
}
diff --git a/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/pom.xml b/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/pom.xml
index aa9310aad..64732a72d 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/pom.xml
+++ b/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/pom.xml
@@ -48,5 +48,11 @@
org.apache.tika
tika-core
+
+ org.eclipse.digitaltwin.basyx
+
+ basyx.filerepository-backend-mongodb
+
+
\ No newline at end of file
diff --git a/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/MongoDBSubmodelRepositoryConfiguration.java b/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/MongoDBSubmodelRepositoryConfiguration.java
index 43c6ef743..b8f3342f5 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/MongoDBSubmodelRepositoryConfiguration.java
+++ b/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/MongoDBSubmodelRepositoryConfiguration.java
@@ -24,6 +24,7 @@
******************************************************************************/
package org.eclipse.digitaltwin.basyx.submodelrepository;
+import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository;
import org.eclipse.digitaltwin.basyx.submodelservice.InMemorySubmodelServiceFactory;
import org.eclipse.digitaltwin.basyx.submodelservice.SubmodelServiceFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
@@ -45,6 +46,6 @@
public class MongoDBSubmodelRepositoryConfiguration {
@Bean
public SubmodelServiceFactory getInMemorySubmodelServiceFactory() {
- return new InMemorySubmodelServiceFactory();
+ return new InMemorySubmodelServiceFactory(new InMemoryFileRepository());
}
}
diff --git a/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/SubmodelMongoDBBackendProvider.java b/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/SubmodelMongoDBBackendProvider.java
index bacf23842..cd414bb77 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/SubmodelMongoDBBackendProvider.java
+++ b/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/SubmodelMongoDBBackendProvider.java
@@ -27,14 +27,12 @@
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
import org.eclipse.digitaltwin.basyx.common.mongocore.BasyxMongoMappingContext;
-import org.eclipse.digitaltwin.basyx.core.file.FileRepository;
import org.eclipse.digitaltwin.basyx.submodelrepository.backend.SubmodelBackendProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
-import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.data.mongodb.repository.support.MappingMongoEntityInformation;
import org.springframework.data.mongodb.repository.support.SimpleMongoRepository;
import org.springframework.data.repository.CrudRepository;
@@ -71,13 +69,4 @@ public CrudRepository getCrudRepository() {
return new SimpleMongoRepository<>(new MappingMongoEntityInformation<>(entity), template);
}
- @Override
- public FileRepository getFileRepository() {
- return new MongoDBSubmodelFileRepository(configureDefaultGridFsTemplate(template));
- }
-
- private GridFsTemplate configureDefaultGridFsTemplate(MongoTemplate mongoTemplate) {
- return new GridFsTemplate(mongoTemplate.getMongoDatabaseFactory(), mongoTemplate.getConverter());
- }
-
}
diff --git a/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/TestMongoDBSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/TestMongoDBSubmodelRepository.java
index e394b86d8..5746eecb0 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/TestMongoDBSubmodelRepository.java
+++ b/basyx.submodelrepository/basyx.submodelrepository-backend-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/TestMongoDBSubmodelRepository.java
@@ -26,7 +26,6 @@
import static org.junit.Assert.assertEquals;
-import java.nio.file.Paths;
import java.util.Collection;
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
@@ -34,6 +33,8 @@
import org.eclipse.digitaltwin.basyx.common.mongocore.BasyxMongoMappingContext;
import org.eclipse.digitaltwin.basyx.common.mongocore.MongoDBUtilities;
import org.eclipse.digitaltwin.basyx.core.exceptions.NotInvokableException;
+import org.eclipse.digitaltwin.basyx.core.filerepository.FileRepository;
+import org.eclipse.digitaltwin.basyx.core.filerepository.MongoDBFileRepository;
import org.eclipse.digitaltwin.basyx.submodelrepository.backend.SimpleSubmodelRepositoryFactory;
import org.eclipse.digitaltwin.basyx.submodelrepository.backend.SubmodelBackendProvider;
import org.eclipse.digitaltwin.basyx.submodelrepository.core.SubmodelRepositorySuite;
@@ -41,13 +42,10 @@
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.data.mongodb.core.MongoTemplate;
-import org.springframework.data.mongodb.core.query.Criteria;
-import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
-import com.mongodb.client.gridfs.model.GridFSFile;
public class TestMongoDBSubmodelRepository extends SubmodelRepositorySuite {
private final String COLLECTION = "submodelTestCollection";
@@ -56,15 +54,15 @@ public class TestMongoDBSubmodelRepository extends SubmodelRepositorySuite {
private final MongoTemplate TEMPLATE = new MongoTemplate(CLIENT, "BaSyxTestDb");
private final GridFsTemplate GRIDFS_TEMPLATE = new GridFsTemplate(TEMPLATE.getMongoDatabaseFactory(), TEMPLATE.getConverter());
private static final String CONFIGURED_SM_REPO_NAME = "configured-sm-repo-name";
- private static final String MONGO_ID = "_id";
- private static final String GRIDFS_ID_DELIMITER = "#";
+ private FileRepository fileRepository;
@Override
protected SubmodelRepository getSubmodelRepository() {
MongoDBUtilities.clearCollection(TEMPLATE, COLLECTION);
+ fileRepository = new MongoDBFileRepository(GRIDFS_TEMPLATE);
SubmodelBackendProvider submodelBackendProvider = new SubmodelMongoDBBackendProvider(new BasyxMongoMappingContext(), COLLECTION, TEMPLATE);
- SubmodelRepositoryFactory submodelRepositoryFactory = new SimpleSubmodelRepositoryFactory(submodelBackendProvider, new InMemorySubmodelServiceFactory());
+ SubmodelRepositoryFactory submodelRepositoryFactory = new SimpleSubmodelRepositoryFactory(submodelBackendProvider, new InMemorySubmodelServiceFactory(fileRepository));
return submodelRepositoryFactory.create();
}
@@ -80,18 +78,13 @@ protected SubmodelRepository getSubmodelRepository(Collection submodel
@Override
protected boolean fileExistsInStorage(String fileValue) {
- String fileId = getFileId(fileValue);
-
- GridFSFile file = GRIDFS_TEMPLATE.findOne(new Query(Criteria.where(MONGO_ID).is(fileId)));
-
- return file != null && GRIDFS_TEMPLATE.getResource(file).exists();
-
+ return fileRepository.exists(fileValue);
}
@Test
public void getConfiguredMongoDBSmRepositoryName() {
SubmodelBackendProvider submodelBackendProvider = new SubmodelMongoDBBackendProvider(new BasyxMongoMappingContext(), COLLECTION, TEMPLATE);
- SubmodelRepository repo = new SimpleSubmodelRepositoryFactory(submodelBackendProvider, new InMemorySubmodelServiceFactory(), CONFIGURED_SM_REPO_NAME).create();
+ SubmodelRepository repo = new SimpleSubmodelRepositoryFactory(submodelBackendProvider, new InMemorySubmodelServiceFactory(fileRepository), CONFIGURED_SM_REPO_NAME).create();
assertEquals(CONFIGURED_SM_REPO_NAME, repo.getName());
}
@@ -119,11 +112,4 @@ private static void addSubmodelsToRepoWithoutInvokableOperations(Collectioncommons-io
commons-io
+
+ org.eclipse.digitaltwin.basyx
+ basyx.filerepository-backend
+
\ No newline at end of file
diff --git a/basyx.submodelrepository/basyx.submodelrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/CrudSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/CrudSubmodelRepository.java
index c8ef370e7..aa5685dd1 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/CrudSubmodelRepository.java
+++ b/basyx.submodelrepository/basyx.submodelrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/CrudSubmodelRepository.java
@@ -25,10 +25,7 @@
package org.eclipse.digitaltwin.basyx.submodelrepository.backend;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
@@ -41,7 +38,6 @@
import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.SerializationException;
import org.eclipse.digitaltwin.aas4j.v3.dataformat.json.JsonDeserializer;
import org.eclipse.digitaltwin.aas4j.v3.dataformat.json.JsonSerializer;
-import org.eclipse.digitaltwin.aas4j.v3.model.File;
import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable;
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
@@ -49,19 +45,14 @@
import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException;
import org.eclipse.digitaltwin.basyx.core.exceptions.ElementNotAFileException;
import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException;
-import org.eclipse.digitaltwin.basyx.core.exceptions.FileHandlingException;
import org.eclipse.digitaltwin.basyx.core.exceptions.IdentificationMismatchException;
import org.eclipse.digitaltwin.basyx.core.exceptions.MissingIdentifierException;
-import org.eclipse.digitaltwin.basyx.core.file.FileMetadata;
-import org.eclipse.digitaltwin.basyx.core.file.FileRepository;
import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult;
import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
import org.eclipse.digitaltwin.basyx.core.pagination.PaginationSupport;
-import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier;
import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository;
import org.eclipse.digitaltwin.basyx.submodelservice.SubmodelService;
import org.eclipse.digitaltwin.basyx.submodelservice.SubmodelServiceFactory;
-import org.eclipse.digitaltwin.basyx.submodelservice.value.FileBlobValue;
import org.eclipse.digitaltwin.basyx.submodelservice.value.SubmodelElementValue;
import org.eclipse.digitaltwin.basyx.submodelservice.value.SubmodelValueOnly;
import org.slf4j.Logger;
@@ -72,7 +63,7 @@
* Default Implementation for the {@link SubmodelRepository} based on Spring
* {@link CrudRepository}
*
- * @author danish
+ * @author danish, mateusmolina
*
*/
public class CrudSubmodelRepository implements SubmodelRepository {
@@ -82,14 +73,12 @@ public class CrudSubmodelRepository implements SubmodelRepository {
private CrudRepository submodelBackend;
private SubmodelServiceFactory submodelServiceFactory;
- private FileRepository fileHandlingBackend;
private String submodelRepositoryName = null;
public CrudSubmodelRepository(SubmodelBackendProvider submodelBackendProvider, SubmodelServiceFactory submodelServiceFactory) {
this.submodelBackend = submodelBackendProvider.getCrudRepository();
this.submodelServiceFactory = submodelServiceFactory;
- this.fileHandlingBackend = submodelBackendProvider.getFileRepository();
}
public CrudSubmodelRepository(SubmodelBackendProvider submodelBackendProvider, SubmodelServiceFactory submodelServiceFactory, String submodelRepositoryName) {
@@ -212,16 +201,6 @@ public void updateSubmodelElement(String submodelId, String idShortPath, Submode
throwIfMismatchingIds(element.getIdShort(), submodelElement.getIdShort());
- if (isFileSubmodelElement(element) && !isFileSubmodelElement(submodelElement)) {
-
- try {
- deleteFileValue(submodelId, idShortPath);
- } catch (FileDoesNotExistException e) {
- logger.info("The Submodel Element with idShortPath '{}' is a File Submodel Element but there is no file attachment associated with this.", idShortPath);
- }
-
- }
-
submodelService.updateSubmodelElement(idShortPath, submodelElement);
updateSubmodel(submodelId, submodelService.getSubmodel());
@@ -231,8 +210,6 @@ public void updateSubmodelElement(String submodelId, String idShortPath, Submode
public void deleteSubmodelElement(String submodelId, String idShortPath) throws ElementDoesNotExistException {
SubmodelService submodelService = getSubmodelServiceOrThrow(submodelId);
- deleteAssociatedFile(submodelId, idShortPath);
-
submodelService.deleteSubmodelElement(idShortPath);
updateSubmodel(submodelId, submodelService.getSubmodel());
@@ -258,105 +235,29 @@ public Submodel getSubmodelByIdMetadata(String submodelId) throws ElementDoesNot
@Override
public java.io.File getFileByPathSubmodel(String submodelId, String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException {
+ SubmodelService submodelService = getSubmodelServiceOrThrow(submodelId);
- SubmodelElement submodelElement = getSubmodelElement(submodelId, idShortPath);
-
- throwIfSmElementIsNotAFile(submodelElement);
-
- File fileSmElement = (File) submodelElement;
- String filePath = getFilePath(fileSmElement);
-
- InputStream fileContent = getFileInputStream(filePath);
-
- return createFile(filePath, fileContent);
+ return submodelService.getFileByPath(idShortPath);
}
@Override
public void setFileValue(String submodelId, String idShortPath, String fileName, InputStream inputStream) throws ElementDoesNotExistException, ElementNotAFileException {
- SubmodelElement submodelElement = getSubmodelElement(submodelId, idShortPath);
-
- throwIfSmElementIsNotAFile(submodelElement);
-
- File fileSmElement = (File) submodelElement;
-
- if (fileHandlingBackend.exists(fileSmElement.getValue()))
- fileHandlingBackend.delete(fileSmElement.getValue());
-
- String uniqueFileName = createUniqueFileName(submodelId, idShortPath, fileName);
-
- FileMetadata fileMetadata = new FileMetadata(uniqueFileName, fileSmElement.getContentType(), inputStream);
-
- String filePath = fileHandlingBackend.save(fileMetadata);
+ SubmodelService submodelService = getSubmodelServiceOrThrow(submodelId);
- FileBlobValue fileValue = new FileBlobValue(fileSmElement.getContentType(), filePath);
+ submodelService.setFileValue(idShortPath, fileName, inputStream);
- setSubmodelElementValue(submodelId, idShortPath, fileValue);
+ updateSubmodel(submodelId, submodelService.getSubmodel());
}
@Override
public void deleteFileValue(String submodelId, String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException {
- SubmodelElement submodelElement = getSubmodelElement(submodelId, idShortPath);
-
- throwIfSmElementIsNotAFile(submodelElement);
-
- File fileSubmodelElement = (File) submodelElement;
- String filePath = fileSubmodelElement.getValue();
-
- fileHandlingBackend.delete(filePath);
+ SubmodelService submodelService = getSubmodelServiceOrThrow(submodelId);
- FileBlobValue fileValue = new FileBlobValue(" ", " ");
+ submodelService.deleteFileValue(idShortPath);
- setSubmodelElementValue(submodelId, idShortPath, fileValue);
+ updateSubmodel(submodelId, submodelService.getSubmodel());
}
- private void deleteAssociatedFile(String submodelId, String idShortPath) {
- try {
- deleteFileValue(submodelId, idShortPath);
- } catch (Exception e) {
- return;
- }
- }
-
- private boolean isFileSubmodelElement(SubmodelElement submodelElement) {
- return submodelElement instanceof File;
- }
-
- private InputStream getFileInputStream(String filePath) {
- InputStream fileContent;
-
- try {
- fileContent = fileHandlingBackend.find(filePath);
- } catch (FileDoesNotExistException e) {
- throw new FileDoesNotExistException(String.format("File at path '%s' could not be found.", filePath));
- }
-
- return fileContent;
- }
-
- private java.io.File createFile(String filePath, InputStream fileIs) {
-
- try {
- byte[] content = fileIs.readAllBytes();
- fileIs.close();
-
- createOutputStream(filePath, content);
-
- return new java.io.File(filePath);
- } catch (IOException e) {
- throw new FileHandlingException("Exception occurred while creating file from the InputStream." + e.getMessage());
- }
-
- }
-
- private void createOutputStream(String filePath, byte[] content) throws IOException {
-
- try (OutputStream outputStream = new FileOutputStream(filePath)) {
- outputStream.write(content);
- } catch (IOException e) {
- throw new FileHandlingException("Exception occurred while creating OutputStream from byte[]." + e.getMessage());
- }
-
- }
private void initializeRemoteCollection(Collection submodels) {
if (submodels == null || submodels.isEmpty())
@@ -394,20 +295,6 @@ private Submodel getSubmodelDeepCopy(Submodel submodel) {
}
}
- private void throwIfSmElementIsNotAFile(SubmodelElement submodelElement) {
-
- if (!isFileSubmodelElement(submodelElement))
- throw new ElementNotAFileException(submodelElement.getIdShort());
- }
-
- private String getFilePath(File fileSubmodelElement) {
- return fileSubmodelElement.getValue();
- }
-
- private String createUniqueFileName(String submodelId, String idShortPath, String fileName) {
- return Base64UrlEncodedIdentifier.encodeIdentifier(submodelId) + "-" + idShortPath.replace("/", "-") + "-" + fileName;
- }
-
private SubmodelService getSubmodelServiceOrThrow(String submodelId) {
Submodel submodel = submodelBackend.findById(submodelId).orElseThrow(() -> new ElementDoesNotExistException(submodelId));
diff --git a/basyx.submodelrepository/basyx.submodelrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/SubmodelBackendProvider.java b/basyx.submodelrepository/basyx.submodelrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/SubmodelBackendProvider.java
index a023570f4..ae3edc9db 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/SubmodelBackendProvider.java
+++ b/basyx.submodelrepository/basyx.submodelrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/SubmodelBackendProvider.java
@@ -26,7 +26,6 @@
package org.eclipse.digitaltwin.basyx.submodelrepository.backend;
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
-import org.eclipse.digitaltwin.basyx.core.file.FileRepository;
import org.springframework.data.repository.CrudRepository;
/**
@@ -38,6 +37,4 @@ public interface SubmodelBackendProvider {
public CrudRepository getCrudRepository();
- public FileRepository getFileRepository();
-
}
diff --git a/basyx.submodelrepository/basyx.submodelrepository-backend/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/CrudSubmodelRepositoryTest.java b/basyx.submodelrepository/basyx.submodelrepository-backend/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/CrudSubmodelRepositoryTest.java
index 8b541a70c..9f3c658dd 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-backend/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/CrudSubmodelRepositoryTest.java
+++ b/basyx.submodelrepository/basyx.submodelrepository-backend/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/CrudSubmodelRepositoryTest.java
@@ -28,7 +28,6 @@
import static org.junit.Assert.assertEquals;
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
-import org.eclipse.digitaltwin.basyx.core.file.FileRepository;
import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository;
import org.junit.Test;
import org.springframework.data.repository.CrudRepository;
@@ -57,10 +56,6 @@ public CrudRepository getCrudRepository() {
return null;
}
- @Override
- public FileRepository getFileRepository() {
- return null;
- }
};
}
diff --git a/basyx.submodelrepository/basyx.submodelrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/core/SubmodelRepositorySubmodelServiceWrapper.java b/basyx.submodelrepository/basyx.submodelrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/core/SubmodelRepositorySubmodelServiceWrapper.java
index 4ffeeadb8..1a0faae40 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/core/SubmodelRepositorySubmodelServiceWrapper.java
+++ b/basyx.submodelrepository/basyx.submodelrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/core/SubmodelRepositorySubmodelServiceWrapper.java
@@ -25,12 +25,16 @@
package org.eclipse.digitaltwin.basyx.submodelrepository.core;
+import java.io.File;
+import java.io.InputStream;
import java.util.List;
import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable;
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.ElementNotAFileException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException;
import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult;
import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository;
@@ -115,4 +119,19 @@ public void patchSubmodelElements(List submodelElementList) {
repoApi.patchSubmodelElements(submodelId, submodelElementList);
}
+ @Override
+ public File getFileByPath(String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException {
+ return repoApi.getFileByPathSubmodel(submodelId, idShortPath);
+ }
+
+ @Override
+ public void setFileValue(String idShortPath, String fileName, InputStream inputStream) throws ElementDoesNotExistException, ElementNotAFileException {
+ repoApi.setFileValue(submodelId, idShortPath, fileName, inputStream);
+ }
+
+ @Override
+ public void deleteFileValue(String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException {
+ repoApi.deleteFileValue(submodelId, idShortPath);
+ }
+
}
diff --git a/basyx.submodelrepository/basyx.submodelrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/core/SubmodelRepositorySuite.java b/basyx.submodelrepository/basyx.submodelrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/core/SubmodelRepositorySuite.java
index fa04eaf90..13a4024a8 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/core/SubmodelRepositorySuite.java
+++ b/basyx.submodelrepository/basyx.submodelrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/core/SubmodelRepositorySuite.java
@@ -26,34 +26,22 @@
package org.eclipse.digitaltwin.basyx.submodelrepository.core;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.io.IOUtils;
import org.eclipse.digitaltwin.aas4j.v3.model.DataTypeDefXsd;
import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable;
import org.eclipse.digitaltwin.aas4j.v3.model.Property;
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
-import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty;
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodel;
import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException;
import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException;
-import org.eclipse.digitaltwin.basyx.core.exceptions.ElementNotAFileException;
-import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException;
import org.eclipse.digitaltwin.basyx.core.exceptions.IdentificationMismatchException;
import org.eclipse.digitaltwin.basyx.core.exceptions.MissingIdentifierException;
import org.eclipse.digitaltwin.basyx.core.exceptions.NotInvokableException;
@@ -66,7 +54,6 @@
import org.eclipse.digitaltwin.basyx.submodelservice.SubmodelServiceSuite;
import org.eclipse.digitaltwin.basyx.submodelservice.value.PropertyValue;
import org.junit.Test;
-import org.springframework.core.io.ClassPathResource;
/**
* Testsuite for implementations of the SubmodelRepository interface
@@ -76,7 +63,6 @@
*/
public abstract class SubmodelRepositorySuite extends SubmodelServiceSuite {
private static final PaginationInfo NO_LIMIT_PAGINATION_INFO = new PaginationInfo(0, null);
- private static final String DUMMY_FILE_CONTENT = "this is a file";
private static final String EMPTY_ID = " ";
private static final String NULL_ID = null;
@@ -87,8 +73,6 @@ protected SubmodelRepository getSubmodelRepository(Collection submodel
submodels.forEach(repo::createSubmodel);
return repo;
}
-
- protected abstract boolean fileExistsInStorage(String fileValue);
@Test
public void getAllSubmodelsPreconfigured() {
@@ -200,94 +184,6 @@ public void createSubmodelCollectionWithMissingId() {
getSubmodelRepository(submodels);
}
-
- @Test
- public void updateNonFileSME() {
- SubmodelRepository repo = getSubmodelRepositoryWithDummySubmodels();
-
- String idShortPathPropertyInSmeCol = SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_SUBMODEL_ELEMENT_COLLECTION_ID_SHORT + "." + SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_PROPERTY_ID_SHORT;
-
- Property newProperty = SubmodelServiceHelper.createDummyProperty(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_PROPERTY_ID_SHORT, "arbitraryValue", DataTypeDefXsd.STRING);
-
- repo.updateSubmodelElement(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, idShortPathPropertyInSmeCol, newProperty);
-
- Property updatedProperty = (Property) repo.getSubmodelElement(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, idShortPathPropertyInSmeCol);
-
- assertEquals(newProperty, updatedProperty);
- }
-
- @Test
- public void updateNonFileSMEWithFileSME() {
- SubmodelRepository repo = getSubmodelRepositoryWithDummySubmodels();
-
- String idShortPathPropertyInSmeCol = SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_SUBMODEL_ELEMENT_COLLECTION_ID_SHORT + "." + SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_PROPERTY_ID_SHORT;
-
- org.eclipse.digitaltwin.aas4j.v3.model.File newFileSME = SubmodelServiceHelper.createDummyFile(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_PROPERTY_ID_SHORT, "text/plain", "arbitraryFileValue");
-
- repo.updateSubmodelElement(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, idShortPathPropertyInSmeCol, newFileSME);
-
- org.eclipse.digitaltwin.aas4j.v3.model.File updatedFile = (org.eclipse.digitaltwin.aas4j.v3.model.File) repo.getSubmodelElement(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, idShortPathPropertyInSmeCol);
-
- assertEquals(newFileSME, updatedFile);
- }
-
- @Test
- public void updateFileSMEWithNonFileSME() throws FileNotFoundException, IOException {
- SubmodelRepository repo = getSubmodelRepositoryWithDummySubmodels();
-
- String idShortPathPropertyInSmeCol = SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_SUBMODEL_ELEMENT_COLLECTION_ID_SHORT + "." + SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT;
-
- String fileName = "SampleJsonFile.json";
-
- repo.setFileValue(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, idShortPathPropertyInSmeCol, fileName, getInputStreamOfFileFromClasspath(fileName));
-
- File file = repo.getFileByPathSubmodel(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, idShortPathPropertyInSmeCol);
-
- assertFileExistsOnPath(file);
-
- Property newProperty = SubmodelServiceHelper.createDummyProperty(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, "4005", DataTypeDefXsd.INT);
-
- repo.updateSubmodelElement(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, idShortPathPropertyInSmeCol, newProperty);
-
- Property updatedProperty = (Property) repo.getSubmodelElement(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, idShortPathPropertyInSmeCol);
-
- assertEquals(newProperty, updatedProperty);
- }
-
- @Test
- public void updateFileSMEWithFileSME() throws FileNotFoundException, IOException {
- SubmodelRepository repo = getSubmodelRepositoryWithDummySubmodels();
-
- String idShortPathPropertyInSmeCol = SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_SUBMODEL_ELEMENT_COLLECTION_ID_SHORT + "." + SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT;
-
- String fileName = "SampleJsonFile.json";
-
- repo.setFileValue(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, idShortPathPropertyInSmeCol, fileName, getInputStreamOfFileFromClasspath(fileName));
-
- File file = repo.getFileByPathSubmodel(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, idShortPathPropertyInSmeCol);
-
- assertFileExistsOnPath(file);
-
- org.eclipse.digitaltwin.aas4j.v3.model.File newFileSME = SubmodelServiceHelper.createDummyFile(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, "text/plain", "someArbitraryPlainText");
-
- repo.updateSubmodelElement(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, idShortPathPropertyInSmeCol, newFileSME);
-
- org.eclipse.digitaltwin.aas4j.v3.model.File updatedFileSME = (org.eclipse.digitaltwin.aas4j.v3.model.File) repo.getSubmodelElement(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, idShortPathPropertyInSmeCol);
-
- assertEquals(newFileSME, updatedFileSME);
- assertFileExistsOnPath(file);
- }
-
- @Test(expected = ElementDoesNotExistException.class)
- public void updateNonExistingSME() {
- SubmodelRepository repo = getSubmodelRepositoryWithDummySubmodels();
-
- String idShortPathPropertyInSmeCol = SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_SUBMODEL_ELEMENT_COLLECTION_ID_SHORT + ".NonExistingSMEIdShort";
-
- Property newNonExistingProperty = SubmodelServiceHelper.createDummyProperty(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_PROPERTY_ID_SHORT, "arbitraryPropertyValue", DataTypeDefXsd.STRING);
-
- repo.updateSubmodelElement(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, idShortPathPropertyInSmeCol, newNonExistingProperty);
- }
@Test
public void deleteSubmodel() {
@@ -338,105 +234,6 @@ public void setPropertyValue() {
assertEquals(expected, retrievedValue.getValue());
}
- @Test
- public void updateFile() {
- SubmodelRepository repo = getSubmodelRepositoryWithDummySubmodels();
-
- // Set the value of the file-submodelelement for the first time
- try {
- repo.setFileValue(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, "SampleJsonFile.json", getInputStreamOfFileFromClasspath("SampleJsonFile.json"));
- } catch (IOException e1) {
- fail();
- e1.printStackTrace();
- }
-
- // Set the value of the file-submodel element again with a dummy text file
- try {
- repo.setFileValue(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, "newFile.txt", getInputStreamOfDummyFile());
- } catch (IOException e1) {
- fail();
- e1.printStackTrace();
- }
-
- // Get the file from the file submodel element
- File retrievedValue = repo.getFileByPathSubmodel(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
-
- try {
- String actual = new String(FileUtils.openInputStream(retrievedValue).readAllBytes());
- assertEquals(DUMMY_FILE_CONTENT, actual);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- @Test
- public void getFile() {
- SubmodelRepository repo = getSubmodelRepositoryWithDummySubmodels();
- String expectedFileExtension = "json";
-
- InputStream expectedFile = null;
- try {
- expectedFile = getInputStreamOfFileFromClasspath("SampleJsonFile.json");
- } catch (IOException e1) {
- e1.printStackTrace();
- }
-
- try {
- repo.setFileValue(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, "SampleJsonFile.json", getInputStreamOfFileFromClasspath("SampleJsonFile.json"));
- } catch (IOException e) {
- fail();
- e.printStackTrace();
- }
-
- File retrievedValue = repo.getFileByPathSubmodel(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
-
- assertEquals(expectedFileExtension, getExtension(retrievedValue.getName()));
-
- try {
- assertTrue(IOUtils.contentEquals(expectedFile, FileUtils.openInputStream(retrievedValue)));
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- @Test(expected = FileDoesNotExistException.class)
- public void getNonExistingFile() {
- SubmodelRepository repo = getSubmodelRepositoryWithDummySubmodels();
- deleteFileIfExisted(repo);
-
- repo.getFileByPathSubmodel(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
- }
-
- @Test(expected = ElementNotAFileException.class)
- public void getFileFromNonFileSME() {
- SubmodelRepository repo = getSubmodelRepositoryWithDummySubmodels();
-
- repo.getFileByPathSubmodel(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_MULTI_LANG_PROP_ID_SHORT);
- }
-
- @Test
- public void deleteFile() throws ElementDoesNotExistException, ElementNotAFileException, FileNotFoundException, IOException {
- SubmodelRepository repo = getSubmodelRepositoryWithDummySubmodels();
-
- repo.setFileValue(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, "SampleJsonFile.json", getInputStreamOfFileFromClasspath("SampleJsonFile.json"));
-
- repo.deleteFileValue(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
-
- try {
- repo.getFileByPathSubmodel(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
- fail();
- } catch (FileDoesNotExistException expected) {
- }
- }
-
- @Test(expected = FileDoesNotExistException.class)
- public void deleteNonExistingFile() throws IOException {
- SubmodelRepository repo = getSubmodelRepositoryWithDummySubmodels();
- deleteFileIfExisted(repo);
-
- repo.deleteFileValue(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
- }
-
@Test(expected = ElementDoesNotExistException.class)
public void setSubmodelElementValueOfNonExistingSubmodel() {
SubmodelRepository repo = getSubmodelRepositoryWithDummySubmodels();
@@ -446,24 +243,6 @@ public void setSubmodelElementValueOfNonExistingSubmodel() {
repo.setSubmodelElementValue("nonExisting", "doesNotMatter", valueToWrite);
}
- @Test
- public void deleteFileSubmodelElementDeletesFile() throws ElementDoesNotExistException, ElementNotAFileException, FileNotFoundException, IOException {
- SubmodelRepository repo = getSubmodelRepositoryWithDummySubmodels();
-
- final String filename = "SampleJsonFile.json";
-
- repo.setFileValue(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, filename, getInputStreamOfFileFromClasspath(filename));
-
- SubmodelElement submodelElement = repo.getSubmodelElement(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
- String fileValue = ((org.eclipse.digitaltwin.aas4j.v3.model.File) submodelElement).getValue();
-
- assertTrue(fileExistsInStorage(fileValue));
-
- repo.deleteSubmodelElement(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
-
- assertFalse(fileExistsInStorage(fileValue));
- }
-
@Test
public void getDefaultSubmodelRepositoryName() {
SubmodelRepository repo = getSubmodelRepository();
@@ -502,21 +281,6 @@ public void invokeNonOperation() {
submodelRepo.invokeOperation(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_ANNOTATED_RELATIONSHIP_ELEMENT_ID_SHORT, new OperationVariable[0]);
}
-
- private void assertFileExistsOnPath(File file) {
-
- assertTrue(file.exists());
- }
-
- private void deleteFileIfExisted(SubmodelRepository repo) {
- try {
- repo.getFileByPathSubmodel(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
- repo.deleteFileValue(DummySubmodelFactory.SUBMODEL_TECHNICAL_DATA_ID, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
- } catch (FileDoesNotExistException e) {
- return;
- }
-
- }
private Submodel buildDummySubmodel(String id) {
return new DefaultSubmodel.Builder().id(id).submodelElements(new DefaultProperty.Builder().idShort("prop").value("testValue").valueType(DataTypeDefXsd.STRING).build()).build();
@@ -536,20 +300,6 @@ private void assertIsEmpty(Collection submodels) {
assertTrue(submodels.isEmpty());
}
- private InputStream getInputStreamOfFileFromClasspath(String fileName) throws FileNotFoundException, IOException {
- ClassPathResource classPathResource = new ClassPathResource(fileName);
-
- return classPathResource.getInputStream();
- }
-
- private InputStream getInputStreamOfDummyFile() throws FileNotFoundException, IOException {
- return new ByteArrayInputStream(DUMMY_FILE_CONTENT.getBytes());
- }
-
- private String getExtension(String filename) {
- return FilenameUtils.getExtension(filename);
- }
-
@Override
public SubmodelService getSubmodelService(Submodel submodel) {
return new SubmodelRepositorySubmodelServiceWrapper(getSubmodelRepository(), submodel);
diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/TestMqttSubmodelObserver.java b/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/TestMqttSubmodelObserver.java
index 1156d074a..1198e9ab1 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/TestMqttSubmodelObserver.java
+++ b/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/TestMqttSubmodelObserver.java
@@ -43,6 +43,7 @@
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodel;
import org.eclipse.digitaltwin.basyx.common.mqttcore.encoding.Base64URLEncoder;
import org.eclipse.digitaltwin.basyx.common.mqttcore.serializer.SubmodelElementSerializer;
+import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository;
import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelInMemoryBackendProvider;
import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository;
import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepositoryFactory;
@@ -196,7 +197,7 @@ private SubmodelElement createSubmodelElementDummy(String submodelElementId) {
}
private static SubmodelRepository createMqttSubmodelRepository(MqttClient client) {
- SubmodelRepositoryFactory repoFactory = new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory());
+ SubmodelRepositoryFactory repoFactory = new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory(new InMemoryFileRepository()));
return new MqttSubmodelRepositoryFactory(repoFactory, client, new MqttSubmodelRepositoryTopicFactory(new Base64URLEncoder())).create();
}
diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-operation-delegation/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/operation/delegation/TestOperationDelegationFeature.java b/basyx.submodelrepository/basyx.submodelrepository-feature-operation-delegation/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/operation/delegation/TestOperationDelegationFeature.java
index 8f7bad44c..1aa094112 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-feature-operation-delegation/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/operation/delegation/TestOperationDelegationFeature.java
+++ b/basyx.submodelrepository/basyx.submodelrepository-feature-operation-delegation/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/operation/delegation/TestOperationDelegationFeature.java
@@ -26,6 +26,7 @@
package org.eclipse.digitaltwin.basyx.submodelrepository.feature.operation.delegation;
import static org.junit.Assert.assertArrayEquals;
+
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
@@ -43,6 +44,7 @@
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodel;
import org.eclipse.digitaltwin.basyx.InvokableOperation;
import org.eclipse.digitaltwin.basyx.core.exceptions.OperationDelegationException;
+import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository;
import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
import org.eclipse.digitaltwin.basyx.http.Aas4JHTTPSerializationExtension;
import org.eclipse.digitaltwin.basyx.http.BaSyxHTTPConfiguration;
@@ -58,15 +60,14 @@
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockserver.model.HttpStatusCode;
+import org.springframework.http.codec.json.Jackson2JsonDecoder;
+import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
-import org.springframework.http.codec.json.Jackson2JsonDecoder;
-import org.springframework.http.codec.json.Jackson2JsonEncoder;
-
/**
* Tests the {@link OperationDelegationSubmodelRepository} feature
*
@@ -189,7 +190,7 @@ private void createExpectationsForPost(String path, String requestBody, String e
}
private static SubmodelRepository createOperationDelegationSubmodelRepository(OperationDelegation operationDelegation) {
- SubmodelRepositoryFactory repoFactory = new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory());
+ SubmodelRepositoryFactory repoFactory = new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory(new InMemoryFileRepository()));
return new OperationDelegationSubmodelRepositoryFactory(repoFactory, operationDelegation).create();
}
diff --git a/basyx.submodelrepository/basyx.submodelrepository-http/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/http/testconfig/DummySubmodelRepositoryConfig.java b/basyx.submodelrepository/basyx.submodelrepository-http/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/http/testconfig/DummySubmodelRepositoryConfig.java
index af57719fa..88934fe35 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-http/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/http/testconfig/DummySubmodelRepositoryConfig.java
+++ b/basyx.submodelrepository/basyx.submodelrepository-http/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/http/testconfig/DummySubmodelRepositoryConfig.java
@@ -25,6 +25,7 @@
package org.eclipse.digitaltwin.basyx.submodelrepository.http.testconfig;
+import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository;
import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelInMemoryBackendProvider;
import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository;
import org.eclipse.digitaltwin.basyx.submodelrepository.backend.SimpleSubmodelRepositoryFactory;
@@ -45,6 +46,6 @@ public class DummySubmodelRepositoryConfig {
@Bean
@ConditionalOnMissingBean
public SubmodelRepository createSubmodelRepository() {
- return new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory()).create();
+ return new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory(new InMemoryFileRepository())).create();
}
}
diff --git a/basyx.submodelservice/basyx.submodelservice-backend-inmemory/pom.xml b/basyx.submodelservice/basyx.submodelservice-backend-inmemory/pom.xml
index 62858bb06..8f3759f2f 100644
--- a/basyx.submodelservice/basyx.submodelservice-backend-inmemory/pom.xml
+++ b/basyx.submodelservice/basyx.submodelservice-backend-inmemory/pom.xml
@@ -17,6 +17,10 @@
org.eclipse.digitaltwin.basyx
basyx.submodelservice-core
+
+ org.eclipse.digitaltwin.basyx
+ basyx.filerepository-backend-inmemory
+
org.eclipse.digitaltwin.basyx
basyx.submodelservice-core
@@ -32,5 +36,6 @@
org.springframework.boot
spring-boot-starter
+
\ No newline at end of file
diff --git a/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelService.java b/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelService.java
index f3c1aa9e6..3f0101f93 100644
--- a/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelService.java
+++ b/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelService.java
@@ -25,10 +25,15 @@
package org.eclipse.digitaltwin.basyx.submodelservice;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.List;
import java.util.TreeMap;
import java.util.stream.Collectors;
+import org.eclipse.digitaltwin.aas4j.v3.model.File;
import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable;
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
@@ -37,21 +42,27 @@
import org.eclipse.digitaltwin.basyx.InvokableOperation;
import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException;
import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.ElementNotAFileException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.FileHandlingException;
import org.eclipse.digitaltwin.basyx.core.exceptions.NotInvokableException;
+import org.eclipse.digitaltwin.basyx.core.filerepository.FileMetadata;
+import org.eclipse.digitaltwin.basyx.core.filerepository.FileRepository;
import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult;
import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
import org.eclipse.digitaltwin.basyx.core.pagination.PaginationSupport;
+import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier;
import org.eclipse.digitaltwin.basyx.submodelservice.pathparsing.HierarchicalSubmodelElementParser;
import org.eclipse.digitaltwin.basyx.submodelservice.pathparsing.SubmodelElementIdShortHelper;
+import org.eclipse.digitaltwin.basyx.submodelservice.value.FileBlobValue;
import org.eclipse.digitaltwin.basyx.submodelservice.value.SubmodelElementValue;
import org.eclipse.digitaltwin.basyx.submodelservice.value.factory.SubmodelElementValueMapperFactory;
import org.eclipse.digitaltwin.basyx.submodelservice.value.mapper.ValueMapper;
-
/**
* Implements the SubmodelService as in-memory variant
*
- * @author schnicke, danish
+ * @author schnicke, danish, mateusmolina
*
*/
public class InMemorySubmodelService implements SubmodelService {
@@ -60,13 +71,16 @@ public class InMemorySubmodelService implements SubmodelService {
private HierarchicalSubmodelElementParser parser;
private SubmodelElementIdShortHelper helper = new SubmodelElementIdShortHelper();
+ private final FileRepository fileRepository;
+
/**
* Creates the InMemory SubmodelService containing the passed Submodel
*
* @param submodel
*/
- public InMemorySubmodelService(Submodel submodel) {
+ public InMemorySubmodelService(Submodel submodel, FileRepository fileRepository) {
this.submodel = submodel;
+ this.fileRepository = fileRepository;
parser = new HierarchicalSubmodelElementParser(submodel);
}
@@ -79,11 +93,9 @@ public Submodel getSubmodel() {
public CursorResult> getSubmodelElements(PaginationInfo pInfo) {
List allSubmodels = submodel.getSubmodelElements();
- TreeMap submodelMap = allSubmodels.stream()
- .collect(Collectors.toMap(SubmodelElement::getIdShort, aas -> aas, (a, b) -> a, TreeMap::new));
+ TreeMap submodelMap = allSubmodels.stream().collect(Collectors.toMap(SubmodelElement::getIdShort, aas -> aas, (a, b) -> a, TreeMap::new));
- PaginationSupport paginationSupport = new PaginationSupport<>(submodelMap,
- SubmodelElement::getIdShort);
+ PaginationSupport paginationSupport = new PaginationSupport<>(submodelMap, SubmodelElement::getIdShort);
CursorResult> paginatedSubmodels = paginationSupport.getPaged(pInfo);
return paginatedSubmodels;
}
@@ -96,7 +108,7 @@ public SubmodelElement getSubmodelElement(String idShortPath) throws ElementDoes
@Override
public SubmodelElementValue getSubmodelElementValue(String idShort) throws ElementDoesNotExistException {
SubmodelElementValueMapperFactory submodelElementValueFactory = new SubmodelElementValueMapperFactory();
-
+
return submodelElementValueFactory.create(getSubmodelElement(idShort)).getValue();
}
@@ -104,26 +116,26 @@ public SubmodelElementValue getSubmodelElementValue(String idShort) throws Eleme
@Override
public void setSubmodelElementValue(String idShort, SubmodelElementValue value) throws ElementDoesNotExistException {
SubmodelElementValueMapperFactory submodelElementValueFactory = new SubmodelElementValueMapperFactory();
-
+
ValueMapper valueMapper = submodelElementValueFactory.create(getSubmodelElement(idShort));
-
- valueMapper.setValue(value);
+
+ valueMapper.setValue(value);
}
@Override
public void createSubmodelElement(SubmodelElement submodelElement) throws CollidingIdentifierException {
throwIfSubmodelElementExists(submodelElement.getIdShort());
-
+
List smElements = submodel.getSubmodelElements();
smElements.add(submodelElement);
submodel.setSubmodelElements(smElements);
}
-
+
private void throwIfSubmodelElementExists(String submodelElementId) {
try {
getSubmodelElement(submodelElementId);
- throw new CollidingIdentifierException(submodelElementId);
- }catch(ElementDoesNotExistException e) {
+ throw new CollidingIdentifierException(submodelElementId);
+ } catch (ElementDoesNotExistException e) {
return;
}
}
@@ -131,9 +143,9 @@ private void throwIfSubmodelElementExists(String submodelElementId) {
@Override
public void createSubmodelElement(String idShortPath, SubmodelElement submodelElement) throws ElementDoesNotExistException, CollidingIdentifierException {
throwIfSubmodelElementExists(getFullIdShortPath(idShortPath, submodelElement.getIdShort()));
-
+
SubmodelElement parentSme = parser.getSubmodelElementFromIdShortPath(idShortPath);
- if(parentSme instanceof SubmodelElementList) {
+ if (parentSme instanceof SubmodelElementList) {
SubmodelElementList list = (SubmodelElementList) parentSme;
List submodelElements = list.getValue();
submodelElements.add(submodelElement);
@@ -148,18 +160,20 @@ public void createSubmodelElement(String idShortPath, SubmodelElement submodelEl
return;
}
}
-
+
@Override
public void updateSubmodelElement(String idShortPath, SubmodelElement submodelElement) {
deleteSubmodelElement(idShortPath);
-
+
String idShortPathParentSME = parser.getIdShortPathOfParentElement(idShortPath);
-
+
createSubmodelElement(idShortPathParentSME, submodelElement);
}
@Override
public void deleteSubmodelElement(String idShortPath) throws ElementDoesNotExistException {
+ deleteAssociatedFileIfAny(idShortPath);
+
if (!helper.isNestedIdShortPath(idShortPath)) {
deleteFlatSubmodelElement(idShortPath);
return;
@@ -169,7 +183,7 @@ public void deleteSubmodelElement(String idShortPath) throws ElementDoesNotExist
private void deleteNestedSubmodelElement(String idShortPath) {
SubmodelElement sm = parser.getSubmodelElementFromIdShortPath(idShortPath);
- if(helper.isDirectParentASubmodelElementList(idShortPath)) {
+ if (helper.isDirectParentASubmodelElementList(idShortPath)) {
deleteNestedSubmodelElementFromList(idShortPath, sm);
} else {
deleteNestedSubmodelElementFromCollection(idShortPath, sm);
@@ -184,8 +198,7 @@ private void deleteNestedSubmodelElementFromList(String idShortPath, SubmodelEle
private void deleteNestedSubmodelElementFromCollection(String idShortPath, SubmodelElement sm) {
String collectionId = helper.extractDirectParentSubmodelElementCollectionIdShort(idShortPath);
- SubmodelElementCollection collection = (SubmodelElementCollection) parser
- .getSubmodelElementFromIdShortPath(collectionId);
+ SubmodelElementCollection collection = (SubmodelElementCollection) parser.getSubmodelElementFromIdShortPath(collectionId);
collection.getValue().remove(sm);
}
@@ -210,14 +223,14 @@ private int findIndexOfElementTobeDeleted(String idShortPath) {
@Override
public OperationVariable[] invokeOperation(String idShortPath, OperationVariable[] input) {
SubmodelElement sme = getSubmodelElement(idShortPath);
-
+
if (!(sme instanceof InvokableOperation))
throw new NotInvokableException(idShortPath);
-
+
InvokableOperation operation = (InvokableOperation) sme;
return operation.invoke(input);
}
-
+
private String getFullIdShortPath(String idShortPath, String submodelElementId) {
return idShortPath + "." + submodelElementId;
}
@@ -226,4 +239,120 @@ private String getFullIdShortPath(String idShortPath, String submodelElementId)
public void patchSubmodelElements(List submodelElementList) {
this.submodel.setSubmodelElements(submodelElementList);
}
+
+ @Override
+ public java.io.File getFileByPath(String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException {
+ SubmodelElement submodelElement = getSubmodelElement(idShortPath);
+
+ throwIfSmElementIsNotAFile(submodelElement);
+
+ File fileSmElement = (File) submodelElement;
+ String filePath = getFilePath(fileSmElement);
+
+ InputStream fileContent = getFileInputStream(filePath);
+
+ return createFile(filePath, fileContent);
+ }
+
+ @Override
+ public void setFileValue(String idShortPath, String fileName, InputStream inputStream) throws ElementDoesNotExistException, ElementNotAFileException {
+ SubmodelElement submodelElement = getSubmodelElement(idShortPath);
+
+ throwIfSmElementIsNotAFile(submodelElement);
+
+ File fileSmElement = (File) submodelElement;
+
+ if (fileRepository.exists(fileSmElement.getValue()))
+ fileRepository.delete(fileSmElement.getValue());
+
+ String uniqueFileName = createUniqueFileName(idShortPath, fileName);
+
+ FileMetadata fileMetadata = new FileMetadata(uniqueFileName, fileSmElement.getContentType(), inputStream);
+
+ String filePath = fileRepository.save(fileMetadata);
+
+ FileBlobValue fileValue = new FileBlobValue(fileSmElement.getContentType(), filePath);
+
+ setSubmodelElementValue(idShortPath, fileValue);
+
+ }
+
+ @Override
+ public void deleteFileValue(String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException {
+ SubmodelElement submodelElement = getSubmodelElement(idShortPath);
+
+ throwIfSmElementIsNotAFile(submodelElement);
+
+ File fileSubmodelElement = (File) submodelElement;
+ String filePath = fileSubmodelElement.getValue();
+
+ fileRepository.delete(filePath);
+
+ FileBlobValue fileValue = new FileBlobValue(" ", " ");
+
+ setSubmodelElementValue(idShortPath, fileValue);
+ }
+
+ private void deleteAssociatedFileIfAny(String idShortPath) {
+ try {
+ deleteFileValue(idShortPath);
+ } catch (Exception e) {
+ }
+ }
+
+ private boolean isFileSubmodelElement(SubmodelElement submodelElement) {
+ return submodelElement instanceof File;
+ }
+
+ private InputStream getFileInputStream(String filePath) {
+ InputStream fileContent;
+
+ try {
+ fileContent = fileRepository.find(filePath);
+ } catch (FileDoesNotExistException e) {
+ throw new FileDoesNotExistException(String.format("File at path '%s' could not be found.", filePath));
+ }
+
+ return fileContent;
+ }
+
+ private java.io.File createFile(String filePath, InputStream fileIs) {
+
+ try {
+ byte[] content = fileIs.readAllBytes();
+ fileIs.close();
+
+ createOutputStream(filePath, content);
+
+ return new java.io.File(filePath);
+ } catch (IOException e) {
+ throw new FileHandlingException("Exception occurred while creating file from the InputStream." + e.getMessage());
+ }
+
+ }
+
+ private String getFilePath(File fileSubmodelElement) {
+ return fileSubmodelElement.getValue();
+ }
+
+ private String createUniqueFileName(String idShortPath, String fileName) {
+ return Base64UrlEncodedIdentifier.encodeIdentifier(submodel.getId()) + "-" + idShortPath.replace("/", "-") + "-" + fileName;
+ }
+
+ private void throwIfSmElementIsNotAFile(SubmodelElement submodelElement) {
+
+ if (!isFileSubmodelElement(submodelElement))
+ throw new ElementNotAFileException(submodelElement.getIdShort());
+ }
+
+ private void createOutputStream(String filePath, byte[] content) throws IOException {
+
+ try (OutputStream outputStream = new FileOutputStream(filePath)) {
+ outputStream.write(content);
+ } catch (IOException e) {
+ throw new FileHandlingException("Exception occurred while creating OutputStream from byte[]." + e.getMessage());
+ }
+
+ }
+
}
diff --git a/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelServiceFactory.java b/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelServiceFactory.java
index 08bf6fff3..2ae48ef10 100644
--- a/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelServiceFactory.java
+++ b/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelServiceFactory.java
@@ -27,22 +27,29 @@
package org.eclipse.digitaltwin.basyx.submodelservice;
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
+import org.eclipse.digitaltwin.basyx.core.filerepository.FileRepository;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
/**
* SubmodelService factory returning an in-memory backend SubmodelService
*
- * @author schnicke
+ * @author schnicke, mateusmolina
*
*/
@ConditionalOnExpression("'${basyx.submodelservice.backend}'.equals('InMemory') or '${basyx.backend}'.equals('InMemory')")
@Component
public class InMemorySubmodelServiceFactory implements SubmodelServiceFactory {
+ private final FileRepository fileRepository;
+
+ public InMemorySubmodelServiceFactory(FileRepository fileRepository) {
+ this.fileRepository = fileRepository;
+ }
+
@Override
public SubmodelService create(Submodel submodel) {
- return new InMemorySubmodelService(submodel);
+ return new InMemorySubmodelService(submodel, fileRepository);
}
}
diff --git a/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/TestInMemorySubmodelService.java b/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/TestInMemorySubmodelService.java
index 2bc6be649..60926d00d 100644
--- a/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/TestInMemorySubmodelService.java
+++ b/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/TestInMemorySubmodelService.java
@@ -27,6 +27,7 @@
package org.eclipse.digitaltwin.basyx.submodelservice;
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
+import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository;
/**
*
@@ -37,7 +38,13 @@ public class TestInMemorySubmodelService extends SubmodelServiceSuite {
@Override
protected SubmodelService getSubmodelService(Submodel submodel) {
- return new InMemorySubmodelServiceFactory().create(submodel);
+ return new InMemorySubmodelServiceFactory(new InMemoryFileRepository()).create(submodel);
}
+ @Override
+ protected boolean fileExistsInStorage(String fileValue) {
+ java.io.File file = new java.io.File(fileValue);
+
+ return file.exists();
+ }
}
diff --git a/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/ConnectedSubmodelService.java b/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/ConnectedSubmodelService.java
index 02068b489..77d614432 100644
--- a/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/ConnectedSubmodelService.java
+++ b/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/ConnectedSubmodelService.java
@@ -26,6 +26,8 @@
package org.eclipse.digitaltwin.basyx.submodelservice.client;
+import java.io.File;
+import java.io.InputStream;
import java.util.List;
import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable;
@@ -33,7 +35,9 @@
import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
import org.eclipse.digitaltwin.basyx.client.internal.ApiException;
import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.ElementNotAFileException;
import org.eclipse.digitaltwin.basyx.core.exceptions.FeatureNotImplementedException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException;
import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult;
import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
import org.eclipse.digitaltwin.basyx.submodelservice.SubmodelService;
@@ -148,4 +152,19 @@ public void patchSubmodelElements(List submodelElementList) {
throw new FeatureNotImplementedException();
}
+ @Override
+ public File getFileByPath(String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException {
+ throw new FeatureNotImplementedException();
+ }
+
+ @Override
+ public void setFileValue(String idShortPath, String fileName, InputStream inputStream) throws ElementDoesNotExistException, ElementNotAFileException {
+ throw new FeatureNotImplementedException();
+ }
+
+ @Override
+ public void deleteFileValue(String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException {
+ throw new FeatureNotImplementedException();
+ }
+
}
diff --git a/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestConnectedSubmodelService.java b/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestConnectedSubmodelService.java
index 2a2216333..b5051fa2a 100644
--- a/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestConnectedSubmodelService.java
+++ b/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestConnectedSubmodelService.java
@@ -27,6 +27,8 @@
package org.eclipse.digitaltwin.basyx.submodelservice.client;
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
+import org.eclipse.digitaltwin.basyx.core.exceptions.ElementNotAFileException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException;
import org.eclipse.digitaltwin.basyx.core.exceptions.NotInvokableException;
import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier;
import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository;
@@ -75,6 +77,13 @@ protected SubmodelService getSubmodelService(Submodel submodel) {
return new ConnectedSubmodelService("http://localhost:8080/submodels/" + base64UrlEncodedId);
}
+ @Override
+ protected boolean fileExistsInStorage(String fileValue) {
+ java.io.File file = new java.io.File(fileValue);
+
+ return file.exists();
+ }
+
@Override
public void getSubmodelElements() {
// TODO Auto-generated method stub
@@ -105,4 +114,57 @@ public void invokeNonOperation() {
throw new NotInvokableException();
}
+ @Override
+ public void deleteFileSubmodelElementDeletesFile() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void updateFileSMEWithNonFileSME() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void updateFileSMEWithFileSME() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void getNonExistingFile() {
+ throw new FileDoesNotExistException();
+
+ }
+
+ @Override
+ public void deleteNonExistingFile() {
+ throw new FileDoesNotExistException();
+
+ }
+
+ @Override
+ public void getFileFromNonFileSME() {
+ throw new ElementNotAFileException();
+
+ }
+
+ @Override
+ public void deleteFile() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void getFile() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void updateFile() {
+ // TODO Auto-generated method stub
+
+ }
}
diff --git a/basyx.submodelservice/basyx.submodelservice-core/pom.xml b/basyx.submodelservice/basyx.submodelservice-core/pom.xml
index 4848c6a3e..4183c527d 100644
--- a/basyx.submodelservice/basyx.submodelservice-core/pom.xml
+++ b/basyx.submodelservice/basyx.submodelservice-core/pom.xml
@@ -30,5 +30,10 @@
org.eclipse.digitaltwin.basyx
basyx.http
+
+ commons-io
+ commons-io
+ test
+
\ No newline at end of file
diff --git a/basyx.submodelservice/basyx.submodelservice-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelService.java b/basyx.submodelservice/basyx.submodelservice-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelService.java
index 8bf2a383c..776b2b909 100644
--- a/basyx.submodelservice/basyx.submodelservice-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelService.java
+++ b/basyx.submodelservice/basyx.submodelservice-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelService.java
@@ -25,12 +25,15 @@
package org.eclipse.digitaltwin.basyx.submodelservice;
+import java.io.InputStream;
import java.util.List;
import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable;
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.ElementNotAFileException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException;
import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult;
import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
import org.eclipse.digitaltwin.basyx.submodelservice.value.SubmodelElementValue;
@@ -38,7 +41,7 @@
/**
* Specifies the overall SubmodelService API
*
- * @author schnicke
+ * @author schnicke, mateusmolina
*
*/
public interface SubmodelService {
@@ -143,4 +146,43 @@ public interface SubmodelService {
*/
public OperationVariable[] invokeOperation(String idShortPath, OperationVariable[] input) throws ElementDoesNotExistException;
+ /**
+ * Retrieves the file of a file submodelelement
+ *
+ * @param idShortPath
+ * the IdShort path of the file element
+ *
+ * @throws ElementDoesNotExistException
+ * @throws ElementNotAFileException
+ * @throws FileDoesNotExistException
+ */
+ public java.io.File getFileByPath(String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException;
+
+ /**
+ * Uploads a file to a file submodelelement
+ *
+ * @param idShortPath
+ * the IdShort path of the file element
+ * @param fileName
+ * the file name
+ * @param inputStream
+ * the inputStream of the file to be uploaded
+ *
+ * @throws ElementDoesNotExistException
+ * @throws ElementNotAFileException
+ */
+ public void setFileValue(String idShortPath, String fileName, InputStream inputStream) throws ElementDoesNotExistException, ElementNotAFileException;
+
+ /**
+ * Deletes the file of a file submodelelement
+ *
+ * @param idShortPath
+ * the IdShort path of the file element
+ *
+ * @throws ElementDoesNotExistException
+ * @throws ElementNotAFileException
+ * @throws FileDoesNotExistException
+ */
+ public void deleteFileValue(String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException;
+
}
diff --git a/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceHelper.java b/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceHelper.java
index 5b7df0c55..ab3bcae87 100644
--- a/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceHelper.java
+++ b/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceHelper.java
@@ -127,6 +127,7 @@ public class SubmodelServiceHelper {
public static final String SUBMODEL_TECHNICAL_DATA_FILE_CATEGORY = "PARAMETER";
public static final String SUBMODEL_TECHNICAL_DATA_FILE_VALUE = "testFile.json";
public static final String SUBMODEL_TECHNICAL_DATA_FILE_CONTENT_TYPE = "application/json";
+ public static final String SUBMODEL_TECHNICAL_NON_FILE = "application/json";
// SUBMODEL_ELEMENT_B_DATA
public static final String SUBMODEL_TECHNICAL_DATA_BLOB_ID_SHORT = "BlobData";
@@ -329,7 +330,8 @@ public static SubmodelElementList createSubmodelElementList() {
public static List getAllSubmodelElements() {
List list = new ArrayList<>();
list.addAll(
- Lists.newArrayList(createPropertySubmodelElement(), createRangeSubmodelElement(), createMultiLanguagePropertySubmodelElement(), createFileSubmodelElement(), createEntitySubmodelElement(), createReferenceElementSubmodelElement(),
+ Lists.newArrayList(createPropertySubmodelElement(), createRangeSubmodelElement(), createMultiLanguagePropertySubmodelElement(), createFileSubmodelElement(), createEntitySubmodelElement(),
+ createReferenceElementSubmodelElement(),
createRelationshipElementSubmodelElement(), createAnnotatedRelationshipElementSubmodelElement(), createBlobSubmodelElement(), createSubmodelElementCollection(), createSubmodelElementList(),
createInvokableOperation()));
return list;
diff --git a/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceSuite.java b/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceSuite.java
index 4b2c7f4c7..a599d6b90 100644
--- a/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceSuite.java
+++ b/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceSuite.java
@@ -26,13 +26,20 @@
package org.eclipse.digitaltwin.basyx.submodelservice;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
import org.eclipse.digitaltwin.aas4j.v3.model.DataTypeDefXsd;
import org.eclipse.digitaltwin.aas4j.v3.model.Entity;
import org.eclipse.digitaltwin.aas4j.v3.model.File;
@@ -50,6 +57,8 @@
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementCollection;
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementList;
import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.ElementNotAFileException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException;
import org.eclipse.digitaltwin.basyx.core.exceptions.NotInvokableException;
import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult;
import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
@@ -62,13 +71,25 @@
/**
* Testsuite for implementations of the SubmodelService interface
*
- * @author schnicke, danish
+ * @author schnicke, danish, mateusmolina
*
*/
public abstract class SubmodelServiceSuite {
protected static final PaginationInfo NO_LIMIT_PAGINATION_INFO = new PaginationInfo(0, null);
+
protected abstract SubmodelService getSubmodelService(Submodel submodel);
+ /**
+ * SubmodelService independent way to check if a file exists in storage
+ *
+ * @param fileValue
+ * @return
+ */
+ protected abstract boolean fileExistsInStorage(String fileValue);
+
+ private static final String DUMMY_JSON_1 = "{\"name\":\"SampleJsonFile\",\"description\":\"A JSON file for verification\",\"version\":1}";
+ private static final String DUMMY_JSON_2 = "{\"name\":\"SampleJsonFile\",\"description\":\"A JSON file for verification\",\"version\":2}";
+
@Test
public void getSubmodel() {
Submodel technicalData = DummySubmodelFactory.createTechnicalDataSubmodel();
@@ -82,8 +103,7 @@ public void getSubmodelElements() {
Submodel technicalData = DummySubmodelFactory.createTechnicalDataSubmodel();
SubmodelService smService = getSubmodelService(technicalData);
- assertTrue(technicalData.getSubmodelElements()
- .containsAll(smService.getSubmodelElements(NO_LIMIT_PAGINATION_INFO).getResult()));
+ assertTrue(technicalData.getSubmodelElements().containsAll(smService.getSubmodelElements(NO_LIMIT_PAGINATION_INFO).getResult()));
}
@Test
@@ -127,11 +147,7 @@ public void getHierarchicalSubmodelElementWhenFirstElementIsList() {
SubmodelElementList submodelElementList = new DefaultSubmodelElementList();
submodelElementList.setIdShort("testList");
List listElements = new ArrayList<>();
- Property testProperty = new DefaultProperty.Builder().idShort("propIdShort")
- .category("cat1")
- .value("123")
- .valueType(DataTypeDefXsd.INTEGER)
- .build();
+ Property testProperty = new DefaultProperty.Builder().idShort("propIdShort").category("cat1").value("123").valueType(DataTypeDefXsd.INTEGER).build();
listElements.add(testProperty);
submodelElementList.setValue(listElements);
submodelElementsList.add(submodelElementList);
@@ -261,12 +277,7 @@ public void getRangeValue() {
public void getMultiLanguagePropertyValue() {
Submodel technicalData = DummySubmodelFactory.createTechnicalDataSubmodel();
- List expectedValue = Arrays.asList(new DefaultLangStringTextType.Builder().text("Hello")
- .language("en")
- .build(),
- new DefaultLangStringTextType.Builder().text("Hallo")
- .language("de")
- .build());
+ List expectedValue = Arrays.asList(new DefaultLangStringTextType.Builder().text("Hello").language("en").build(), new DefaultLangStringTextType.Builder().text("Hallo").language("de").build());
MultiLanguagePropertyValue submodelElementValue = (MultiLanguagePropertyValue) getSubmodelService(technicalData).getSubmodelElementValue(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_MULTI_LANG_PROP_ID_SHORT);
@@ -316,17 +327,9 @@ public void createNestedSubmodelElement() {
Submodel operationDataSubmodel = DummySubmodelFactory.createOperationalDataSubmodelWithHierarchicalSubmodelElements();
SubmodelService submodelService = getSubmodelService(operationDataSubmodel);
- Property propertyInSmeCol = new DefaultProperty.Builder().idShort("test123")
- .category("cat1")
- .value("305")
- .valueType(DataTypeDefXsd.INTEGER)
- .build();
+ Property propertyInSmeCol = new DefaultProperty.Builder().idShort("test123").category("cat1").value("305").valueType(DataTypeDefXsd.INTEGER).build();
- Property propertyInSmeList = new DefaultProperty.Builder().idShort("test456")
- .category("cat1")
- .value("305")
- .valueType(DataTypeDefXsd.INTEGER)
- .build();
+ Property propertyInSmeList = new DefaultProperty.Builder().idShort("test456").category("cat1").value("305").valueType(DataTypeDefXsd.INTEGER).build();
String idShortPathPropertyInSmeCol = DummySubmodelFactory.SUBMODEL_OPERATIONAL_DATA_ELEMENT_COLLECTION_ID_SHORT;
submodelService.createSubmodelElement(idShortPathPropertyInSmeCol, propertyInSmeCol);
@@ -342,51 +345,93 @@ public void createNestedSubmodelElement() {
SubmodelElement propertyInSmeListCreated = submodelService.getSubmodelElement(idShortPathPropertyInSmeList);
assertEquals("test456", propertyInSmeListCreated.getIdShort());
}
-
+
@Test
public void updateNonFileSME() {
Submodel technicalSubmodel = DummySubmodelFactory.createTechnicalDataSubmodel();
SubmodelService submodelService = getSubmodelService(technicalSubmodel);
-
+
String idShortPathPropertyInSmeCol = SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_SUBMODEL_ELEMENT_COLLECTION_ID_SHORT + "." + SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_PROPERTY_ID_SHORT;
-
+
Property newProperty = SubmodelServiceHelper.createDummyProperty(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_PROPERTY_ID_SHORT, "arbitraryValue", DataTypeDefXsd.STRING);
-
+
submodelService.updateSubmodelElement(idShortPathPropertyInSmeCol, newProperty);
-
+
Property updatedProperty = (Property) submodelService.getSubmodelElement(idShortPathPropertyInSmeCol);
-
+
assertEquals(newProperty, updatedProperty);
}
-
+
@Test
public void updateNonFileSMEWithFileSME() {
Submodel technicalSubmodel = DummySubmodelFactory.createTechnicalDataSubmodel();
SubmodelService submodelService = getSubmodelService(technicalSubmodel);
-
+
String idShortPathPropertyInSmeCol = SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_SUBMODEL_ELEMENT_COLLECTION_ID_SHORT + "." + SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_PROPERTY_ID_SHORT;
-
+
org.eclipse.digitaltwin.aas4j.v3.model.File newFileSME = SubmodelServiceHelper.createDummyFile(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_PROPERTY_ID_SHORT, "text/plain", "arbitraryFileValue");
-
+
submodelService.updateSubmodelElement(idShortPathPropertyInSmeCol, newFileSME);
-
+
org.eclipse.digitaltwin.aas4j.v3.model.File updatedFile = (org.eclipse.digitaltwin.aas4j.v3.model.File) submodelService.getSubmodelElement(idShortPathPropertyInSmeCol);
-
+
assertEquals(newFileSME, updatedFile);
}
-
+
@Test(expected = ElementDoesNotExistException.class)
public void updateNonExistingSME() {
Submodel technicalSubmodel = DummySubmodelFactory.createTechnicalDataSubmodel();
SubmodelService submodelService = getSubmodelService(technicalSubmodel);
-
+
String idShortPathPropertyInSmeCol = SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_SUBMODEL_ELEMENT_COLLECTION_ID_SHORT + "." + "NonExistingSMEIdShort";
-
+
Property newNonExistingProperty = SubmodelServiceHelper.createDummyProperty(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_PROPERTY_ID_SHORT, "arbitraryPropertyValue", DataTypeDefXsd.STRING);
-
+
submodelService.updateSubmodelElement(idShortPathPropertyInSmeCol, newNonExistingProperty);
}
+ @Test
+ public void updateFileSMEWithNonFileSME() throws FileNotFoundException, IOException {
+ Submodel technicalDataSubmodel = DummySubmodelFactory.createTechnicalDataSubmodel();
+ SubmodelService submodelService = getSubmodelService(technicalDataSubmodel);
+
+ String idShortPathPropertyInSmeCol = SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_SUBMODEL_ELEMENT_COLLECTION_ID_SHORT + "." + SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT;
+
+ submodelService.setFileValue(idShortPathPropertyInSmeCol, "jsonFile1.json", getInputStreamOfDummyFile(DUMMY_JSON_1));
+
+ String fileValue = ((File) submodelService.getSubmodelElement(idShortPathPropertyInSmeCol)).getValue();
+
+ assertTrue(fileExistsInStorage(fileValue));
+
+ Property newProperty = SubmodelServiceHelper.createDummyProperty(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, "4005", DataTypeDefXsd.INT);
+
+ submodelService.updateSubmodelElement(idShortPathPropertyInSmeCol, newProperty);
+
+ Property updatedProperty = (Property) submodelService.getSubmodelElement(idShortPathPropertyInSmeCol);
+
+ assertEquals(newProperty, updatedProperty);
+ assertFalse(fileExistsInStorage(fileValue));
+ }
+
+ @Test
+ public void updateFileSMEWithFileSME() throws FileNotFoundException, IOException {
+ Submodel technicalDataSubmodel = DummySubmodelFactory.createTechnicalDataSubmodel();
+ SubmodelService submodelService = getSubmodelService(technicalDataSubmodel);
+
+ String idShortPathPropertyInSmeCol = SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_SUBMODEL_ELEMENT_COLLECTION_ID_SHORT + "." + SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT;
+
+ submodelService.setFileValue(idShortPathPropertyInSmeCol, "jsonFile1.json", getInputStreamOfDummyFile(DUMMY_JSON_1));
+
+ assertStoredFileContentEquals(submodelService, idShortPathPropertyInSmeCol, DUMMY_JSON_1);
+
+ File newFileSME = SubmodelServiceHelper.createDummyFile(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, "text/plain", "someArbitraryPlainText");
+ submodelService.updateSubmodelElement(idShortPathPropertyInSmeCol, newFileSME);
+
+ submodelService.setFileValue(idShortPathPropertyInSmeCol, "jsonFile2.json", getInputStreamOfDummyFile(DUMMY_JSON_2));
+
+ assertStoredFileContentEquals(submodelService, idShortPathPropertyInSmeCol, DUMMY_JSON_2);
+ }
+
@Test
public void deleteNestedSubmodelElementInSubmodelElementCollection() {
Submodel operationDataSubmodel = DummySubmodelFactory.createOperationalDataSubmodelWithHierarchicalSubmodelElements();
@@ -421,8 +466,7 @@ public void deleteNestedSubmodelElementInSubmodelElementList() {
public void getPaginatedSubmodelElement() {
Submodel technicalData = DummySubmodelFactory.createTechnicalDataSubmodel();
SubmodelService submodelService = getSubmodelService(technicalData);
- CursorResult> cursorResult = submodelService
- .getSubmodelElements(new PaginationInfo(1, ""));
+ CursorResult> cursorResult = submodelService.getSubmodelElements(new PaginationInfo(1, ""));
assertEquals(1, cursorResult.getResult().size());
}
@@ -430,8 +474,7 @@ public void getPaginatedSubmodelElement() {
public void paginationCursor() {
Submodel technicalData = DummySubmodelFactory.createTechnicalDataSubmodel();
SubmodelService submodelService = getSubmodelService(technicalData);
- CursorResult> cursorResult = submodelService.getSubmodelElements(new PaginationInfo(1,
- SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_ANNOTATED_RELATIONSHIP_ELEMENT_ID_SHORT));
+ CursorResult> cursorResult = submodelService.getSubmodelElements(new PaginationInfo(1, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_ANNOTATED_RELATIONSHIP_ELEMENT_ID_SHORT));
assertEquals(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_BLOB_ID_SHORT, cursorResult.getCursor());
}
@@ -440,13 +483,13 @@ public void paginationCursor() {
public void invokeOperation() {
Submodel invokableSubmodel = DummySubmodelFactory.createSubmodelWithAllSubmodelElements();
SubmodelService submodelService = getSubmodelService(invokableSubmodel);
-
+
Property val = new DefaultProperty.Builder().idShort("in").value("2").build();
-
+
OperationVariable[] result = submodelService.invokeOperation(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_OPERATION_ID, new OperationVariable[] { SubmodelServiceHelper.createOperationVariable(val) });
Property ret = (Property) result[0].getValue();
-
+
assertEquals("4", ret.getValue());
}
@@ -459,6 +502,101 @@ public void invokeNonOperation() {
submodelService.invokeOperation(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_ANNOTATED_RELATIONSHIP_ELEMENT_ID_SHORT, new OperationVariable[0]);
}
+ @Test
+ public void deleteFileSubmodelElementDeletesFile() throws ElementDoesNotExistException, ElementNotAFileException, FileNotFoundException, IOException {
+ Submodel technicalDataSubmodel = DummySubmodelFactory.createTechnicalDataSubmodel();
+ SubmodelService submodelService = getSubmodelService(technicalDataSubmodel);
+
+ submodelService.setFileValue(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, "jsonFile1.json", getInputStreamOfDummyFile(DUMMY_JSON_1));
+
+ SubmodelElement submodelElement = submodelService.getSubmodelElement(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
+ String fileValue = ((File) submodelElement).getValue();
+
+ assertTrue(fileExistsInStorage(fileValue));
+
+ submodelService.deleteSubmodelElement(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
+
+ assertFalse(fileExistsInStorage(fileValue));
+ }
+
+ @Test
+ public void getFile() throws FileNotFoundException, IOException {
+ Submodel technicalDataSubmodel = DummySubmodelFactory.createTechnicalDataSubmodel();
+ SubmodelService submodelService = getSubmodelService(technicalDataSubmodel);
+ String expectedFileExtension = "json";
+
+ submodelService.setFileValue(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, "jsonFile1.json", getInputStreamOfDummyFile(DUMMY_JSON_1));
+
+ java.io.File retrievedValue = submodelService.getFileByPath(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
+
+ assertEquals(expectedFileExtension, getExtension(retrievedValue.getName()));
+ assertStoredFileContentEquals(submodelService, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, DUMMY_JSON_1);
+ }
+
+ @Test(expected = FileDoesNotExistException.class)
+ public void getNonExistingFile() {
+ Submodel technicalDataSubmodel = DummySubmodelFactory.createTechnicalDataSubmodel();
+ SubmodelService submodelService = getSubmodelService(technicalDataSubmodel);
+ deleteFileIfExisted(submodelService, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
+
+ submodelService.getFileByPath(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
+ }
+
+ @Test
+ public void deleteFile() throws ElementDoesNotExistException, ElementNotAFileException, FileNotFoundException, IOException {
+ Submodel technicalDataSubmodel = DummySubmodelFactory.createTechnicalDataSubmodel();
+ SubmodelService submodelService = getSubmodelService(technicalDataSubmodel);
+
+ submodelService.setFileValue(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, "jsonFile1.json", getInputStreamOfDummyFile(DUMMY_JSON_1));
+
+ submodelService.deleteFileValue(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
+
+ try {
+ submodelService.getFileByPath(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
+ fail();
+ } catch (FileDoesNotExistException expected) {
+ }
+ }
+
+ @Test
+ public void updateFile() throws ElementDoesNotExistException, ElementNotAFileException, FileNotFoundException, IOException {
+ Submodel technicalDataSubmodel = DummySubmodelFactory.createTechnicalDataSubmodel();
+ SubmodelService submodelService = getSubmodelService(technicalDataSubmodel);
+
+ submodelService.setFileValue(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, "jsonFile1.json", getInputStreamOfDummyFile(DUMMY_JSON_1));
+
+ assertStoredFileContentEquals(submodelService, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, DUMMY_JSON_1);
+
+ submodelService.setFileValue(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, "jsonFile2.json", getInputStreamOfDummyFile(DUMMY_JSON_2));
+
+ assertStoredFileContentEquals(submodelService, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, DUMMY_JSON_2);
+ }
+
+ @Test(expected = ElementNotAFileException.class)
+ public void getFileFromNonFileSME() {
+ Submodel technicalDataSubmodel = DummySubmodelFactory.createTechnicalDataSubmodel();
+ SubmodelService submodelService = getSubmodelService(technicalDataSubmodel);
+
+ submodelService.getFileByPath(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_MULTI_LANG_PROP_ID_SHORT);
+ }
+
+ @Test(expected = FileDoesNotExistException.class)
+ public void deleteNonExistingFile() throws IOException {
+ Submodel technicalDataSubmodel = DummySubmodelFactory.createTechnicalDataSubmodel();
+ SubmodelService submodelService = getSubmodelService(technicalDataSubmodel);
+ deleteFileIfExisted(submodelService, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
+
+ submodelService.deleteFileValue(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT);
+ }
+
+ private void assertStoredFileContentEquals(SubmodelService submodelService, String fileIdShort, String content) throws IOException {
+ java.io.File retrievedValue = submodelService.getFileByPath(fileIdShort);
+
+ String actual = new String(FileUtils.openInputStream(retrievedValue).readAllBytes());
+
+ assertEquals(content, actual);
+ }
+
private List createHierarchicalSubmodelElement() {
List submodelElementsCollection = new ArrayList<>();
@@ -493,28 +631,37 @@ private String generateNestedIdShortPath() {
}
private DefaultSubmodelElementList createDummySubmodelElementList(String idShort) {
- return new DefaultSubmodelElementList.Builder().idShort(idShort)
- .build();
+ return new DefaultSubmodelElementList.Builder().idShort(idShort).build();
}
private SubmodelElementCollection createDummySubmodelElementCollection(String idShort) {
- return new DefaultSubmodelElementCollection.Builder().idShort(idShort)
- .build();
+ return new DefaultSubmodelElementCollection.Builder().idShort(idShort).build();
}
private DefaultEntity createDummyEntityWithStatement(SubmodelElement submodelElement, String idShort) {
- return new DefaultEntity.Builder().idShort(idShort)
- .category("cat1")
- .statements(submodelElement)
- .build();
+ return new DefaultEntity.Builder().idShort(idShort).category("cat1").statements(submodelElement).build();
}
private DefaultProperty createDummyProperty(String idShort) {
- return new DefaultProperty.Builder().idShort(idShort)
- .category("cat1")
- .value("123")
- .valueType(DataTypeDefXsd.INTEGER)
- .build();
+ return new DefaultProperty.Builder().idShort(idShort).category("cat1").value("123").valueType(DataTypeDefXsd.INTEGER).build();
+ }
+
+ private InputStream getInputStreamOfDummyFile(String fileContent) throws FileNotFoundException, IOException {
+ return new ByteArrayInputStream(fileContent.getBytes());
+ }
+
+ private String getExtension(String filename) {
+ return FilenameUtils.getExtension(filename);
}
+
+ private void deleteFileIfExisted(SubmodelService service, String idShort) {
+ try {
+ service.getFileByPath(idShort);
+ service.deleteFileValue(idShort);
+ } catch (FileDoesNotExistException e) {
+ return;
+ }
+
+ }
}
diff --git a/basyx.submodelservice/basyx.submodelservice-http/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceHTTPApi.java b/basyx.submodelservice/basyx.submodelservice-http/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceHTTPApi.java
index 8e12e20eb..93c6b1dd2 100644
--- a/basyx.submodelservice/basyx.submodelservice-http/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceHTTPApi.java
+++ b/basyx.submodelservice/basyx.submodelservice-http/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceHTTPApi.java
@@ -37,6 +37,7 @@
import org.eclipse.digitaltwin.basyx.pagination.GetSubmodelElementsResult;
import org.eclipse.digitaltwin.basyx.submodelservice.value.SubmodelElementValue;
import org.eclipse.digitaltwin.basyx.submodelservice.value.SubmodelValueOnly;
+import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
@@ -44,6 +45,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;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -322,4 +325,55 @@ ResponseEntity putSubmodelElementByPath(@Parameter(in = ParameterIn.PATH,
ResponseEntity patchSubmodelValueOnly(@Parameter(in = ParameterIn.DEFAULT, description = "Submodel object in its ValueOnly representation", required = false, schema = @Schema()) @Valid @RequestBody List body,
@Parameter(in = ParameterIn.QUERY, description = "Determines the structural depth of the respective resource content", schema = @Schema(allowableValues = {
"deep" }, defaultValue = "deep")) @Valid @RequestParam(value = "level", required = false, defaultValue = "deep") String level);
+
+ @Operation(summary = "Deletes file content of an existing submodel element at a specified path within submodel elements hierarchy", description = "", tags = { "Submodel API" })
+ @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Submodel element 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 = "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 = "/submodel/submodel-elements/{idShortPath}/attachment", produces = { "application/json" }, method = RequestMethod.DELETE)
+ ResponseEntity deleteFileByPath(@Parameter(in = ParameterIn.PATH, description = "IdShort path to the submodel element (dot-separated)", required = true, schema = @Schema()) @PathVariable("idShortPath") String idShortPath);
+
+ @Operation(summary = "Downloads file content from a specific submodel element from the Submodel at a specified path", description = "", tags = { "Submodel API" })
+ @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Requested file", 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 = "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 = "405", description = "Method not allowed - Download only valid for File submodel element", 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 = "/submodel/submodel-elements/{idShortPath}/attachment", produces = { "application/octet-stream", "application/json" }, method = RequestMethod.GET)
+ ResponseEntity getFileByPath(@Parameter(in = ParameterIn.PATH, description = "IdShort path to the submodel element (dot-separated)", required = true, schema = @Schema()) @PathVariable("idShortPath") String idShortPath);
+
+ @Operation(summary = "Uploads file content to an existing submodel element at a specified path within submodel elements hierarchy", description = "", tags = { "Submodel API" })
+ @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "Submodel element 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 = "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 = "405", description = "Method not allowed - Upload only valid for File submodel element", 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 = "/submodel/submodel-elements/{idShortPath}/attachment", produces = { "application/json" }, consumes = { "multipart/form-data" }, method = RequestMethod.PUT)
+ ResponseEntity putFileByPath(@Parameter(in = ParameterIn.PATH, description = "IdShort path to the submodel element (dot-separated)", required = true, schema = @Schema()) @PathVariable("idShortPath") String idShortPath,
+ @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.submodelservice/basyx.submodelservice-http/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceHTTPApiController.java b/basyx.submodelservice/basyx.submodelservice-http/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceHTTPApiController.java
index 67b340fc9..2d309dfe9 100644
--- a/basyx.submodelservice/basyx.submodelservice-http/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceHTTPApiController.java
+++ b/basyx.submodelservice/basyx.submodelservice-http/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceHTTPApiController.java
@@ -25,6 +25,8 @@
package org.eclipse.digitaltwin.basyx.submodelservice.http;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
@@ -34,6 +36,9 @@
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultOperationResult;
+import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.ElementNotAFileException;
+import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException;
import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult;
import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
import org.eclipse.digitaltwin.basyx.http.pagination.Base64UrlEncodedCursor;
@@ -44,12 +49,15 @@
import org.eclipse.digitaltwin.basyx.submodelservice.value.SubmodelElementValue;
import org.eclipse.digitaltwin.basyx.submodelservice.value.SubmodelValueOnly;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.FileSystemResource;
+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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
@@ -248,4 +256,55 @@ private String getEncodedCursorFromCursorResult(CursorResult> cursorResult) {
return Base64UrlEncodedCursor.encodeCursor(cursorResult.getCursor());
}
+ @Override
+ public ResponseEntity getFileByPath(String idShortPath) {
+ Resource resource = new FileSystemResource(service.getFileByPath(idShortPath));
+
+ return new ResponseEntity<>(resource, HttpStatus.OK);
+ }
+
+ @Override
+ public ResponseEntity putFileByPath(String idShortPath, String fileName, @Valid MultipartFile file) {
+ InputStream fileInputstream = null;
+ try {
+ fileInputstream = file.getInputStream();
+ service.setFileValue(idShortPath, fileName, fileInputstream);
+ closeInputStream(fileInputstream);
+ return new ResponseEntity<>(HttpStatus.OK);
+ } catch (ElementDoesNotExistException e) {
+ closeInputStream(fileInputstream);
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ } catch (ElementNotAFileException e) {
+ closeInputStream(fileInputstream);
+ return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
+ } catch (IOException e) {
+ closeInputStream(fileInputstream);
+ return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @Override
+ public ResponseEntity deleteFileByPath(String idShortPath) {
+ try {
+ service.deleteFileValue(idShortPath);
+ return new ResponseEntity<>(HttpStatus.OK);
+ } catch (FileDoesNotExistException | ElementDoesNotExistException e) {
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ } catch (ElementNotAFileException e) {
+ return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
+ }
+ }
+
+ private void closeInputStream(InputStream fileInputstream) {
+ if (fileInputstream == null) {
+ return;
+ }
+
+ try {
+ fileInputstream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
}
diff --git a/basyx.submodelservice/basyx.submodelservice-http/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/http/DummySubmodelServiceComponent.java b/basyx.submodelservice/basyx.submodelservice-http/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/http/DummySubmodelServiceComponent.java
index 5d31f9c80..5bccb1449 100644
--- a/basyx.submodelservice/basyx.submodelservice-http/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/http/DummySubmodelServiceComponent.java
+++ b/basyx.submodelservice/basyx.submodelservice-http/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/http/DummySubmodelServiceComponent.java
@@ -25,6 +25,7 @@
package org.eclipse.digitaltwin.basyx.submodelservice.http;
+import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository;
import org.eclipse.digitaltwin.basyx.submodelservice.DummySubmodelFactory;
import org.eclipse.digitaltwin.basyx.submodelservice.InMemorySubmodelServiceFactory;
import org.eclipse.digitaltwin.basyx.submodelservice.SubmodelService;
@@ -36,7 +37,7 @@ public class DummySubmodelServiceComponent {
@Bean
public SubmodelService getSubmodelService() {
- return new InMemorySubmodelServiceFactory()
+ return new InMemorySubmodelServiceFactory(new InMemoryFileRepository())
.create(DummySubmodelFactory.createSubmodelWithAllSubmodelElements());
}
}
\ No newline at end of file
diff --git a/basyx.submodelservice/basyx.submodelservice-http/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceSubmodelElementsTestSuiteHTTP.java b/basyx.submodelservice/basyx.submodelservice-http/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceSubmodelElementsTestSuiteHTTP.java
index 16750e70d..23bcd0ec9 100644
--- a/basyx.submodelservice/basyx.submodelservice-http/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceSubmodelElementsTestSuiteHTTP.java
+++ b/basyx.submodelservice/basyx.submodelservice-http/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceSubmodelElementsTestSuiteHTTP.java
@@ -25,20 +25,34 @@
package org.eclipse.digitaltwin.basyx.submodelservice.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.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.eclipse.digitaltwin.aas4j.v3.model.Submodel;
import org.eclipse.digitaltwin.basyx.http.pagination.Base64UrlEncodedCursor;
import org.eclipse.digitaltwin.basyx.http.serialization.BaSyxHttpTestUtils;
import org.eclipse.digitaltwin.basyx.submodelservice.DummySubmodelFactory;
import org.eclipse.digitaltwin.basyx.submodelservice.SubmodelServiceHelper;
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;
@@ -503,7 +517,125 @@ public void invokeOperation() throws FileNotFoundException, IOException, ParseEx
BaSyxHttpTestUtils.assertSameJSONContent(expectedValue, BaSyxHttpTestUtils.getResponseAsString(response));
}
+
+ @Test
+ public void updateFileSMEWithNonFileSME() throws FileNotFoundException, IOException, ParseException {
+ String element = getJSONValueAsString("PropertySubmodelElementUpdateWithNewIdShort.json");
+
+ String idShortPathPropertyInSmeCol = SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_SUBMODEL_ELEMENT_COLLECTION_ID_SHORT + "." + SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT;
+
+ uploadFileToSubmodelElement(idShortPathPropertyInSmeCol);
+
+ CloseableHttpResponse fileResponse = BaSyxHttpTestUtils.executeGetOnURL(createSMEFileGetURL(idShortPathPropertyInSmeCol));
+ assertEquals(HttpStatus.OK.value(), fileResponse.getCode());
+
+ CloseableHttpResponse updatedResponse = updateElement(createSpecificSubmodelElementURL(idShortPathPropertyInSmeCol), element);
+ assertEquals(HttpStatus.NO_CONTENT.value(), updatedResponse.getCode());
+
+ CloseableHttpResponse fetchedResponse = BaSyxHttpTestUtils.executeGetOnURL(createSpecificSubmodelElementURL(idShortPathPropertyInSmeCol));
+ BaSyxHttpTestUtils.assertSameJSONContent(element, BaSyxHttpTestUtils.getResponseAsString(fetchedResponse));
+ }
+
+ @Test
+ public void updateFileSMEWithFileSME() throws FileNotFoundException, IOException, ParseException {
+ String element = getJSONValueAsString("FileSubmodelElementUpdateWithNewIdShort.json");
+
+ String idShortPathPropertyInSmeCol = SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_SUBMODEL_ELEMENT_COLLECTION_ID_SHORT + "." + SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT;
+
+ uploadFileToSubmodelElement(idShortPathPropertyInSmeCol);
+
+ CloseableHttpResponse fileResponse = BaSyxHttpTestUtils.executeGetOnURL(createSMEFileGetURL(idShortPathPropertyInSmeCol));
+ assertEquals(HttpStatus.OK.value(), fileResponse.getCode());
+
+ CloseableHttpResponse updatedResponse = updateElement(createSpecificSubmodelElementURL(idShortPathPropertyInSmeCol), element);
+ assertEquals(HttpStatus.NO_CONTENT.value(), updatedResponse.getCode());
+
+ CloseableHttpResponse fetchedResponse = BaSyxHttpTestUtils.executeGetOnURL(createSpecificSubmodelElementURL(idShortPathPropertyInSmeCol));
+ BaSyxHttpTestUtils.assertSameJSONContent(element, BaSyxHttpTestUtils.getResponseAsString(fetchedResponse));
+ }
+
+
+ @Test
+ public void uploadFileToFileSubmodelElement() throws IOException {
+ CloseableHttpResponse submodelElementFileUploadResponse = uploadFileToSubmodelElement(DummySubmodelFactory.SUBMODEL_ELEMENT_FILE_ID_SHORT);
+
+ assertEquals(HttpStatus.OK.value(), submodelElementFileUploadResponse.getCode());
+ }
+
+ @Test
+ public void uploadFileToNonFileSubmodelElement() throws FileNotFoundException, UnsupportedEncodingException, ClientProtocolException, IOException {
+ CloseableHttpResponse submodelElementFileUploadResponse = uploadFileToSubmodelElement(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_PROPERTY_ID_SHORT);
+
+ assertEquals(HttpStatus.PRECONDITION_FAILED.value(), submodelElementFileUploadResponse.getCode());
+ }
+
+ @Test
+ public void uploadFileToNotExistElement() throws FileNotFoundException, UnsupportedEncodingException, ClientProtocolException, IOException {
+ CloseableHttpResponse submodelElementFileUploadResponse = uploadFileToSubmodelElement("ElementNotExist");
+
+ assertEquals(HttpStatus.NOT_FOUND.value(), submodelElementFileUploadResponse.getCode());
+ }
+
+ @Test
+ public void deleteFile() throws FileNotFoundException, IOException {
+ uploadFileToSubmodelElement(DummySubmodelFactory.SUBMODEL_ELEMENT_FILE_ID_SHORT);
+
+ CloseableHttpResponse response = BaSyxHttpTestUtils.executeDeleteOnURL(createSMEFileDeleteURL(DummySubmodelFactory.SUBMODEL_ELEMENT_FILE_ID_SHORT));
+ assertEquals(HttpStatus.OK.value(), response.getCode());
+
+ response = BaSyxHttpTestUtils.executeGetOnURL(createSMEFileGetURL(DummySubmodelFactory.SUBMODEL_ELEMENT_FILE_ID_SHORT));
+ assertEquals(HttpStatus.NOT_FOUND.value(), response.getCode());
+ }
+
+ @Test
+ public void deleteFileToNonFileSubmodelElement() throws FileNotFoundException, UnsupportedEncodingException, ClientProtocolException, IOException {
+ uploadFileToSubmodelElement(DummySubmodelFactory.SUBMODEL_ELEMENT_FILE_ID_SHORT);
+
+ CloseableHttpResponse response = BaSyxHttpTestUtils.executeDeleteOnURL(createSMEFileGetURL(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_PROPERTY_ID_SHORT));
+
+ assertEquals(HttpStatus.PRECONDITION_FAILED.value(), response.getCode());
+ }
+
+ @Test
+ public void deleteFileFromNotExistElement() throws FileNotFoundException, UnsupportedEncodingException, ClientProtocolException, IOException {
+ CloseableHttpResponse response = BaSyxHttpTestUtils.executeDeleteOnURL(createSMEFileGetURL("ElementNotExist"));
+
+ assertEquals(HttpStatus.NOT_FOUND.value(), response.getCode());
+ }
+
+ @Test
+ public void getFile() throws FileNotFoundException, IOException, ParseException {
+ String fileName = DummySubmodelFactory.FILE_NAME;
+
+ byte[] expectedFile = readBytesFromClasspath(fileName);
+
+ uploadFileToSubmodelElement(DummySubmodelFactory.SUBMODEL_ELEMENT_FILE_ID_SHORT);
+
+ CloseableHttpResponse response = BaSyxHttpTestUtils.executeGetOnURL(createSMEFileGetURL(DummySubmodelFactory.SUBMODEL_ELEMENT_FILE_ID_SHORT));
+ assertEquals(HttpStatus.OK.value(), response.getCode());
+
+ byte[] actualFile = EntityUtils.toByteArray(response.getEntity());
+
+ response.close();
+
+ assertArrayEquals(expectedFile, actualFile);
+ }
+
+ @Test
+ public void getFileFromNonFileSubmodelElement() throws FileNotFoundException, UnsupportedEncodingException, ClientProtocolException, IOException {
+ CloseableHttpResponse response = BaSyxHttpTestUtils.executeGetOnURL(createSMEFileGetURL(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_PROPERTY_ID_SHORT));
+
+ assertEquals(HttpStatus.PRECONDITION_FAILED.value(), response.getCode());
+ }
+
+ @Test
+ public void getFileFromNotExistElement() throws FileNotFoundException, UnsupportedEncodingException, ClientProtocolException, IOException {
+ CloseableHttpResponse response = BaSyxHttpTestUtils.executeGetOnURL(createSMEFileGetURL("ElementNotExist"));
+
+ assertEquals(HttpStatus.NOT_FOUND.value(), response.getCode());
+ }
+
private CloseableHttpResponse updateElement(String url, String element) throws IOException {
return BaSyxHttpTestUtils.executePutOnURL(url, element);
}
@@ -591,4 +723,57 @@ private String getJSONValueAsString(String fileName) throws FileNotFoundExceptio
return BaSyxHttpTestUtils.readJSONStringFromClasspath(fileName);
}
+ private CloseableHttpResponse uploadFileToSubmodelElement(String submodelElementIdShort) throws IOException {
+ CloseableHttpClient client = HttpClients.createDefault();
+
+ String fileName = DummySubmodelFactory.FILE_NAME;
+
+ java.io.File file = ResourceUtils.getFile("classpath:" + fileName);
+
+ HttpPut putRequest = createPutRequestWithFile(submodelElementIdShort, fileName, file);
+
+ return executePutRequest(client, putRequest);
+ }
+
+ private HttpPut createPutRequestWithFile(String submodelElementIdShort, String fileName, java.io.File file) {
+ HttpPut putRequest = new HttpPut(createSMEFileUploadURL(submodelElementIdShort, fileName));
+
+ MultipartEntityBuilder builder = MultipartEntityBuilder.create();
+
+ builder.addPart("file", new FileBody(file));
+ builder.setContentType(ContentType.MULTIPART_FORM_DATA);
+
+ HttpEntity multipart = builder.build();
+ putRequest.setEntity(multipart);
+ return putRequest;
+ }
+
+ private String createSMEFileUploadURL(String submodelElementIdShort, String fileName) {
+ return getURL() + "/submodel-elements/" + submodelElementIdShort + "/attachment?fileName=" + fileName;
+ }
+
+ private String createSMEFileDeleteURL(String submodelElementIdShort) {
+ return getURL() + "/submodel-elements/" + submodelElementIdShort + "/attachment";
+ }
+
+ private String createSMEFileGetURL( String submodelElementIdShort) {
+ return getURL() + "/submodel-elements/" + submodelElementIdShort + "/attachment";
+ }
+
+
+ private CloseableHttpResponse executePutRequest(CloseableHttpClient client, HttpPut putRequest) throws IOException {
+ CloseableHttpResponse response = client.execute(putRequest);
+
+ HttpEntity responseEntity = response.getEntity();
+
+ EntityUtils.consume(responseEntity);
+ return response;
+ }
+
+ private byte[] readBytesFromClasspath(String fileName) throws FileNotFoundException, IOException {
+ ClassPathResource classPathResource = new ClassPathResource(fileName);
+ InputStream in = classPathResource.getInputStream();
+
+ return in.readAllBytes();
+ }
}
diff --git a/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/BaSyx-Logo.png b/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/BaSyx-Logo.png
new file mode 100644
index 000000000..da613e94c
Binary files /dev/null and b/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/BaSyx-Logo.png differ
diff --git a/basyx.submodelservice/basyx.submodelservice.example/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/example/ExampleSubmodelConfiguration.java b/basyx.submodelservice/basyx.submodelservice.example/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/example/ExampleSubmodelConfiguration.java
index 6041a2191..1f4aecbec 100644
--- a/basyx.submodelservice/basyx.submodelservice.example/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/example/ExampleSubmodelConfiguration.java
+++ b/basyx.submodelservice/basyx.submodelservice.example/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/example/ExampleSubmodelConfiguration.java
@@ -26,6 +26,7 @@
package org.eclipse.digitaltwin.basyx.submodelservice.example;
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
+import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository;
import org.eclipse.digitaltwin.basyx.submodelservice.InMemorySubmodelServiceFactory;
import org.eclipse.digitaltwin.basyx.submodelservice.SubmodelService;
import org.eclipse.digitaltwin.basyx.submodelservice.SubmodelServiceFactory;
@@ -48,7 +49,7 @@ public SubmodelService getSubmodelService(Submodel submodel) {
@Bean
static SubmodelServiceFactory getSubmodelServiceFactory() {
- return new InMemorySubmodelServiceFactory();
+ return new InMemorySubmodelServiceFactory(new InMemoryFileRepository());
}
@Bean
diff --git a/basyx.submodelservice/basyx.submodelservice.example/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/example/DummySubmodelServiceComponent.java b/basyx.submodelservice/basyx.submodelservice.example/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/example/DummySubmodelServiceComponent.java
index c3ba08c40..7482c0ac6 100644
--- a/basyx.submodelservice/basyx.submodelservice.example/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/example/DummySubmodelServiceComponent.java
+++ b/basyx.submodelservice/basyx.submodelservice.example/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/example/DummySubmodelServiceComponent.java
@@ -26,6 +26,7 @@
package org.eclipse.digitaltwin.basyx.submodelservice.example;
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
+import org.eclipse.digitaltwin.basyx.core.filerepository.InMemoryFileRepository;
import org.eclipse.digitaltwin.basyx.submodelservice.DummySubmodelFactory;
import org.eclipse.digitaltwin.basyx.submodelservice.InMemorySubmodelService;
import org.eclipse.digitaltwin.basyx.submodelservice.SubmodelService;
@@ -43,7 +44,7 @@ public class DummySubmodelServiceComponent {
@Bean
public SubmodelService createSubmodelService() {
Submodel submodel = DummySubmodelFactory.createTechnicalDataSubmodel();
- return new InMemorySubmodelService(submodel);
+ return new InMemorySubmodelService(submodel, new InMemoryFileRepository());
}
}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index c470fab58..bed1e095b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -451,6 +451,21 @@
basyx.authorization
${revision}
+
+ org.eclipse.digitaltwin.basyx
+ basyx.filerepository-backend
+ ${revision}
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.filerepository-backend-inmemory
+ ${revision}
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.filerepository-backend-mongodb
+ ${revision}
+
org.eclipse.digitaltwin.basyx
@@ -742,6 +757,18 @@
${revision}
tests
+
+ org.eclipse.digitaltwin.basyx
+ basyx.filerepository-backend
+ ${revision}
+ tests
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.filerepository-backend-inmemory
+ ${revision}
+ tests
+
org.eclipse.digitaltwin.basyx
@@ -842,6 +869,12 @@
${revision}
tests
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasservice-backend-mongodb
+ ${revision}
+ tests
+
org.eclipse.digitaltwin.basyx
basyx.aasservice-feature-mqtt