diff --git a/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/environmentloader/AasEnvironmentLoader.java b/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/environmentloader/AasEnvironmentLoader.java new file mode 100644 index 000000000..b8e8f6381 --- /dev/null +++ b/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/environmentloader/AasEnvironmentLoader.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (C) 2024 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader; + +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.apache.commons.io.FilenameUtils; +import org.eclipse.digitaltwin.aas4j.v3.dataformat.aasx.InMemoryFile; +import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; +import org.eclipse.digitaltwin.aas4j.v3.model.ConceptDescription; +import org.eclipse.digitaltwin.aas4j.v3.model.Environment; +import org.eclipse.digitaltwin.aas4j.v3.model.Submodel; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; +import org.eclipse.digitaltwin.basyx.aasenvironment.FileElementPathCollector; +import org.eclipse.digitaltwin.basyx.aasenvironment.IdShortPathBuilder; +import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.IdentifiableUploader.DelegatingIdentifiableRepository; +import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.IdentifiableUploader.IdentifiableRepository; +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; +import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionRepository; +import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Loader for AAS Environment + * + * @author fried, mateusmolina, despen, witt, jungjan, danish + * + */ +public class AasEnvironmentLoader { + private Logger logger = LoggerFactory.getLogger(AasEnvironmentLoader.class); + private IndentifiableAssertion checker = new IndentifiableAssertion(); + + private AasRepository aasRepository; + private SubmodelRepository submodelRepository; + private ConceptDescriptionRepository conceptDescriptionRepository; + + public AasEnvironmentLoader(AasRepository aasRepository, SubmodelRepository submodelRepository, ConceptDescriptionRepository conceptDescriptionRepository) { + this.aasRepository = aasRepository; + this.submodelRepository = submodelRepository; + this.conceptDescriptionRepository = conceptDescriptionRepository; + } + + public void loadEnvironment(CompleteEnvironment completeEnvironment) { + Environment environment = completeEnvironment.getEnvironment(); + + if (environment == null) + return; + + checker.assertNoDuplicateIds(environment); + + createShellsOnRepositoryFromEnvironment(environment); + createSubmodelsOnRepositoryFromEnvironment(environment, completeEnvironment.getRelatedFiles()); + createConceptDescriptionsOnRepositoryFromEnvironment(environment); + } + + private void createConceptDescriptionsOnRepositoryFromEnvironment(Environment environment) { + IdentifiableRepository repo = new DelegatingIdentifiableRepository(conceptDescriptionRepository::getConceptDescription, conceptDescriptionRepository::updateConceptDescription, + conceptDescriptionRepository::createConceptDescription); + IdentifiableUploader uploader = new IdentifiableUploader(repo); + for (ConceptDescription conceptDescription : environment.getConceptDescriptions()) { + boolean success = uploader.upload(conceptDescription); + logSuccessConceptDescription(conceptDescription.getId(), success); + } + } + + private void createSubmodelsOnRepositoryFromEnvironment(Environment environment, List relatedFiles) { + List submodels = environment.getSubmodels(); + + createSubmodelsOnRepository(submodels); + + if (relatedFiles == null || relatedFiles.isEmpty()) + return; + + for (Submodel submodel : submodels) { + List> idShortElementPathsOfAllFileSMEs = new FileElementPathCollector(submodel).collect(); + + idShortElementPathsOfAllFileSMEs.stream().forEach(fileSMEIdShortPath -> setFileToFileElement(submodel.getId(), fileSMEIdShortPath, relatedFiles)); + } + } + + private void setFileToFileElement(String submodelId, List fileSMEIdShortPathElements, List relatedFiles) { + String fileSMEIdShortPath = new IdShortPathBuilder(new ArrayList<>(fileSMEIdShortPathElements)).build(); + + org.eclipse.digitaltwin.aas4j.v3.model.File fileSME = (org.eclipse.digitaltwin.aas4j.v3.model.File) submodelRepository.getSubmodelElement(submodelId, fileSMEIdShortPath); + + InMemoryFile inMemoryFile = getAssociatedInMemoryFile(relatedFiles, fileSME.getValue()); + + if (inMemoryFile == null) { + logger.info("Unable to set file to the SubmodelElement File with IdShortPath '{}' because it does not exist in the AASX file.", fileSMEIdShortPath); + + return; + } + + submodelRepository.setFileValue(submodelId, fileSMEIdShortPath, getFileName(inMemoryFile.getPath()), new ByteArrayInputStream(inMemoryFile.getFileContent())); + } + + private String getFileName(String path) { + return FilenameUtils.getName(path); + } + + private InMemoryFile getAssociatedInMemoryFile(List relatedFiles, String value) { + + Optional inMemoryFile = relatedFiles.stream().filter(file -> file.getPath().equals(value)).findAny(); + + if (inMemoryFile.isEmpty()) + return null; + + return inMemoryFile.get(); + } + + private void createShellsOnRepositoryFromEnvironment(Environment environment) { + IdentifiableRepository repo = new DelegatingIdentifiableRepository(aasRepository::getAas, aasRepository::updateAas, aasRepository::createAas); + IdentifiableUploader uploader = new IdentifiableUploader<>(repo); + for (AssetAdministrationShell shell : environment.getAssetAdministrationShells()) { + boolean success = uploader.upload(shell); + logSuccess("shell", shell.getId(), success); + } + } + + private void createSubmodelsOnRepository(List submodels) { + IdentifiableRepository repo = new DelegatingIdentifiableRepository(submodelRepository::getSubmodel, submodelRepository::updateSubmodel, submodelRepository::createSubmodel); + IdentifiableUploader uploader = new IdentifiableUploader<>(repo); + for (Submodel submodel : submodels) { + boolean success = uploader.upload(submodel); + logSuccess("submodel", submodel.getId(), success); + } + } + + private void logSuccess(String resourceName, String id, boolean success) { + if (success) { + logger.info("Uploading " + resourceName + " " + id + " was successful!"); + } else { + logger.warn("Uploading " + resourceName + " " + id + " was not successful!"); + } + } + + private void logSuccessConceptDescription(String conceptDescriptionId, boolean success) { + if (!success) { + logger.warn("Colliding Ids detected for ConceptDescription: " + conceptDescriptionId + ". If they are not identical, this is an error. Please note that the already existing ConceptDescription was not updated."); + } else { + logSuccess("conceptDescription", conceptDescriptionId, success); + } + } +} diff --git a/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/environmentloader/CompleteEnvironment.java b/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/environmentloader/CompleteEnvironment.java new file mode 100644 index 000000000..87d4bba5f --- /dev/null +++ b/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/environmentloader/CompleteEnvironment.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (C) 2024 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.eclipse.digitaltwin.aas4j.v3.dataformat.aasx.AASXDeserializer; +import org.eclipse.digitaltwin.aas4j.v3.dataformat.aasx.InMemoryFile; +import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.DeserializationException; +import org.eclipse.digitaltwin.aas4j.v3.dataformat.json.JsonDeserializer; +import org.eclipse.digitaltwin.aas4j.v3.dataformat.xml.XmlDeserializer; +import org.eclipse.digitaltwin.aas4j.v3.model.Environment; + +/** + * Represents an environment and its relatedFiles + * + * @author mateusmolina + * + */ +public class CompleteEnvironment { + private final Environment environment; + private final List relatedFiles; + + public enum EnvironmentType { + AASX, JSON, XML; + + public static EnvironmentType getFromMimeType(String mimeType) { + switch (mimeType) { + case "application/asset-administration-shell-package": + return AASX; + case "application/json": + return JSON; + case "application/xml": + return XML; + case "text/xml": + return XML; + default: + return null; + } + } + + public static EnvironmentType getFromFilePath(String filePath) { + if (filePath.endsWith(".json")) + return JSON; + if (filePath.endsWith(".aasx")) + return AASX; + if (filePath.endsWith(".xml")) + return XML; + return null; + } + + } + + public CompleteEnvironment(Environment environment, List relatedFiles) { + this.environment = environment; + this.relatedFiles = relatedFiles; + } + + public Environment getEnvironment() { + return environment; + } + + public List getRelatedFiles() { + return relatedFiles; + } + + public static CompleteEnvironment fromFile(File file) throws DeserializationException, InvalidFormatException, IOException { + return fromInputStream(new FileInputStream(file), EnvironmentType.getFromFilePath(file.getPath())); + } + + public static CompleteEnvironment fromInputStream(InputStream inputStream, EnvironmentType envType) throws DeserializationException, InvalidFormatException, IOException { + Environment environment = null; + List relatedFiles = null; + + if(envType == EnvironmentType.JSON) { + JsonDeserializer deserializer = new JsonDeserializer(); + environment = deserializer.read(inputStream, Environment.class); + } + if(envType == EnvironmentType.XML) { + XmlDeserializer deserializer = new XmlDeserializer(); + environment = deserializer.read(inputStream); + } + if(envType == EnvironmentType.AASX) { + AASXDeserializer deserializer = new AASXDeserializer(inputStream); + relatedFiles = deserializer.getRelatedFiles(); + environment = deserializer.read(); + } + + return new CompleteEnvironment(environment, relatedFiles); + } +} diff --git a/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/IdentifiableUploader.java b/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/environmentloader/IdentifiableUploader.java similarity index 98% rename from basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/IdentifiableUploader.java rename to basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/environmentloader/IdentifiableUploader.java index ceaf205ad..583c7539e 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/IdentifiableUploader.java +++ b/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/environmentloader/IdentifiableUploader.java @@ -23,7 +23,7 @@ * SPDX-License-Identifier: MIT ******************************************************************************/ -package org.eclipse.digitaltwin.basyx.aasenvironment.preconfiguration; +package org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader; import java.util.Objects; import java.util.Optional; diff --git a/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/IndentifiableAssertion.java b/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/environmentloader/IndentifiableAssertion.java similarity index 97% rename from basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/IndentifiableAssertion.java rename to basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/environmentloader/IndentifiableAssertion.java index d090630f0..5da6d44b0 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/IndentifiableAssertion.java +++ b/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/environmentloader/IndentifiableAssertion.java @@ -22,7 +22,7 @@ * * SPDX-License-Identifier: MIT ******************************************************************************/ -package org.eclipse.digitaltwin.basyx.aasenvironment.preconfiguration; +package org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader; import java.util.HashSet; import java.util.List; diff --git a/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/AasEnvironmentPreconfigurationLoader.java b/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/AasEnvironmentPreconfigurationLoader.java index 220dc1235..2d4c0040d 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/AasEnvironmentPreconfigurationLoader.java +++ b/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/AasEnvironmentPreconfigurationLoader.java @@ -25,34 +25,17 @@ package org.eclipse.digitaltwin.basyx.aasenvironment.preconfiguration; -import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; -import org.apache.commons.io.FilenameUtils; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; -import org.eclipse.digitaltwin.aas4j.v3.dataformat.aasx.AASXDeserializer; -import org.eclipse.digitaltwin.aas4j.v3.dataformat.aasx.InMemoryFile; import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.DeserializationException; -import org.eclipse.digitaltwin.aas4j.v3.dataformat.json.JsonDeserializer; -import org.eclipse.digitaltwin.aas4j.v3.dataformat.xml.XmlDeserializer; -import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; -import org.eclipse.digitaltwin.aas4j.v3.model.ConceptDescription; -import org.eclipse.digitaltwin.aas4j.v3.model.Environment; -import org.eclipse.digitaltwin.aas4j.v3.model.Submodel; -import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; -import org.eclipse.digitaltwin.basyx.aasenvironment.FileElementPathCollector; -import org.eclipse.digitaltwin.basyx.aasenvironment.IdShortPathBuilder; -import org.eclipse.digitaltwin.basyx.aasenvironment.preconfiguration.IdentifiableUploader.DelegatingIdentifiableRepository; -import org.eclipse.digitaltwin.basyx.aasenvironment.preconfiguration.IdentifiableUploader.IdentifiableRepository; -import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; -import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionRepository; -import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; +import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.AasEnvironmentLoader; +import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.CompleteEnvironment; +import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.CompleteEnvironment.EnvironmentType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -77,8 +60,6 @@ public class AasEnvironmentPreconfigurationLoader { private ResourceLoader resourceLoader; - private List relatedFiles; - @Autowired public AasEnvironmentPreconfigurationLoader(ResourceLoader resourceLoader, List pathsToLoad) { this.resourceLoader = resourceLoader; @@ -89,20 +70,19 @@ public boolean shouldLoadPreconfiguredEnvironment() { return pathsToLoad != null; } - public void loadPreconfiguredEnvironments(AasRepository aasRepository, SubmodelRepository submodelRepository, ConceptDescriptionRepository conceptDescriptionRepository) + public void loadPreconfiguredEnvironments(AasEnvironmentLoader environmentLoader) throws IOException, DeserializationException, InvalidFormatException { List files = scanForEnvironments(pathsToLoad); - int numberOfFiles = files.size(); - - IndentifiableAssertion check = new IndentifiableAssertion(); + if (files.isEmpty()) + return; + + int filesCount = files.size(); + int currenFileIndex = 0; - for (int i = 0; i < numberOfFiles; i++) { - File file = files.get(i); - logLoadingProcess(i, numberOfFiles, file.getName()); - Environment environment = getEnvironmentFromFile(file); - check.assertNoDuplicateIds(environment); - addEnvironment(aasRepository, submodelRepository, conceptDescriptionRepository, environment); + for (File file : files) { + logLoadingProcess(currenFileIndex++, filesCount, file.getName()); + environmentLoader.loadEnvironment(CompleteEnvironment.fromFile(file)); } } @@ -116,11 +96,6 @@ private List scanForEnvironments(List pathsToLoad) throws IOExcept return files; } - private void logLoadingProcess(int current, int overall, String fileName) { - int currentForDisplay = current + 1; - logger.info("Loading AAS Environment (" + currentForDisplay + "/" + overall + ") from file " + fileName); - } - private List resolveFiles(List paths) throws IOException { ArrayList files = new ArrayList<>(); @@ -144,13 +119,6 @@ private File getFile(String filePath) throws IOException { .getFile(); } - private void addEnvironment(AasRepository aasRepository, SubmodelRepository submodelRepository, ConceptDescriptionRepository conceptDescriptionRepository, Environment environment) { - if (isEnvironmentLoaded(environment)) { - createShellsOnRepositoryFromEnvironment(aasRepository, environment); - createSubmodelsOnRepositoryFromEnvironment(submodelRepository, environment); - createConceptDescriptionsOnRepositoryFromEnvironment(conceptDescriptionRepository, environment); - } - } private List extractFilesToLoadFromEnvironmentDirectory(String directoryToLoad) throws IllegalArgumentException, IOException { File rootDirectory = getFile(directoryToLoad); @@ -158,133 +126,12 @@ private List extractFilesToLoadFromEnvironmentDirectory(String directoryTo List potentialEnvironments = directoryScanner.listFiles(rootDirectory); return potentialEnvironments.stream() - .filter(file -> isAasxFile(file.getPath()) || isJsonFile(file.getPath()) || isXmlFile(file.getPath())) + .filter(file -> EnvironmentType.getFromFilePath(file.getPath()) != null) .collect(Collectors.toList()); } - private void createConceptDescriptionsOnRepositoryFromEnvironment(ConceptDescriptionRepository cdRepo, Environment environment) { - IdentifiableRepository repo = new DelegatingIdentifiableRepository(cdRepo::getConceptDescription, cdRepo::updateConceptDescription, cdRepo::createConceptDescription); - IdentifiableUploader uploader = new IdentifiableUploader(repo); - for (ConceptDescription eachConceptDescription : environment.getConceptDescriptions()) { - boolean success = uploader.upload(eachConceptDescription); - logSuccessConceptDescription(eachConceptDescription.getId(), success); - } - } - - private void logSuccessConceptDescription(String conceptDescriptionId, boolean success) { - if (!success) { - logger.warn("Colliding Ids detected for ConceptDescription: " + conceptDescriptionId + ". If they are not identical, this is an error. Please note that the already existing ConceptDescription was not updated."); - } else { - logSuccess("conceptDescription", conceptDescriptionId, success); - } - } - - private void createSubmodelsOnRepositoryFromEnvironment(SubmodelRepository submodelRepository, Environment environment) { - List submodels = environment.getSubmodels(); - - createSubmodelsOnRepository(submodelRepository, submodels); - - if (relatedFiles == null || relatedFiles.isEmpty()) - return; - - for (Submodel submodel : submodels) { - List> idShortElementPathsOfAllFileSMEs = new FileElementPathCollector(submodel).collect(); - - idShortElementPathsOfAllFileSMEs.stream().forEach(fileSMEIdShortPath -> setFileToFileElement(submodel.getId(), fileSMEIdShortPath, submodelRepository)); - } - } - - private void setFileToFileElement(String submodelId, List fileSMEIdShortPathElements, SubmodelRepository submodelRepository) { - String fileSMEIdShortPath = new IdShortPathBuilder(new ArrayList<>(fileSMEIdShortPathElements)).build(); - - org.eclipse.digitaltwin.aas4j.v3.model.File fileSME = (org.eclipse.digitaltwin.aas4j.v3.model.File) submodelRepository.getSubmodelElement(submodelId, fileSMEIdShortPath); - - InMemoryFile inMemoryFile = getAssociatedInMemoryFile(relatedFiles, fileSME.getValue()); - - if (inMemoryFile == null) { - logger.info("Unable to set file to the SubmodelElement File with IdShortPath '{}' because it does not exist in the AASX file.", fileSMEIdShortPath); - - return; - } - - submodelRepository.setFileValue(submodelId, fileSMEIdShortPath, getFileName(inMemoryFile.getPath()), new ByteArrayInputStream(inMemoryFile.getFileContent())); + private void logLoadingProcess(int current, int overall, String filename) { + logger.info("Loading AAS Environment ({}/{}) from file '{}'", current, overall, filename); } - private String getFileName(String path) { - return FilenameUtils.getName(path); - } - - private InMemoryFile getAssociatedInMemoryFile(List relatedFiles, String value) { - - Optional inMemoryFile = relatedFiles.stream().filter(file -> file.getPath().equals(value)).findAny(); - - if (inMemoryFile.isEmpty()) - return null; - - return inMemoryFile.get(); - } - - private void createShellsOnRepositoryFromEnvironment(AasRepository aasRepo, Environment environment) { - IdentifiableRepository repo = new DelegatingIdentifiableRepository(aasRepo::getAas, aasRepo::updateAas, aasRepo::createAas); - IdentifiableUploader uploader = new IdentifiableUploader<>(repo); - for (AssetAdministrationShell eachAas : environment.getAssetAdministrationShells()) { - boolean success = uploader.upload(eachAas); - logSuccess("shell", eachAas.getId(), success); - } - } - - private void createSubmodelsOnRepository(SubmodelRepository smRepo, List submodels) { - IdentifiableRepository repo = new DelegatingIdentifiableRepository(smRepo::getSubmodel, smRepo::updateSubmodel, smRepo::createSubmodel); - IdentifiableUploader uploader = new IdentifiableUploader<>(repo); - for (Submodel eachSubmodel : submodels) { - boolean success = uploader.upload(eachSubmodel); - logSuccess("submodel", eachSubmodel.getId(), success); - } - } - - private Environment getEnvironmentFromFile(File file) throws DeserializationException, InvalidFormatException, IOException { - Environment environment = null; - if (isJsonFile(file.getPath())) { - JsonDeserializer deserializer = new JsonDeserializer(); - try (FileInputStream fIn = new FileInputStream(file)) { - environment = deserializer.read(fIn, Environment.class); - } - } else if (isXmlFile(file.getPath())) { - XmlDeserializer deserializer = new XmlDeserializer(); - try (FileInputStream fIn = new FileInputStream(file)) { - environment = deserializer.read(fIn); - } - } else if (isAasxFile(file.getPath())) { - try (FileInputStream fIn = new FileInputStream(file)) { - AASXDeserializer deserializer = new AASXDeserializer(fIn); - relatedFiles = deserializer.getRelatedFiles(); - environment = deserializer.read(); - } - } - return environment; - } - - private static boolean isJsonFile(String filePath) { - return filePath.endsWith(".json"); - } - - private static boolean isXmlFile(String filePath) { - return filePath.endsWith(".xml"); - } - - private static boolean isAasxFile(String filePath) { - return filePath.endsWith(".aasx"); - } - - private boolean isEnvironmentLoaded(Environment environment) { - return environment != null; - } - - private void logSuccess(String resourceName, String id, boolean success) { - if (success) { - logger.info("Uploading " + resourceName + " " + id + " was successful!"); - } else { - logger.warn("Uploading " + resourceName + " " + id + " was not successful!"); - } - } } \ No newline at end of file diff --git a/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/PreconfigurationLoaderInitializer.java b/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/PreconfigurationLoaderInitializer.java index 38bff3bb2..e4db274e2 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/PreconfigurationLoaderInitializer.java +++ b/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/PreconfigurationLoaderInitializer.java @@ -29,9 +29,7 @@ import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.DeserializationException; -import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; -import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionRepository; -import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; +import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.AasEnvironmentLoader; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -45,21 +43,15 @@ @Component public class PreconfigurationLoaderInitializer implements InitializingBean { - private AasRepository aasRepository; - - private SubmodelRepository submodelRepository; - - private ConceptDescriptionRepository conceptDescriptionRepository; + private AasEnvironmentLoader environmentLoader; private AasEnvironmentPreconfigurationLoader preconfigurationLoader; @Autowired - public PreconfigurationLoaderInitializer(AasRepository aasRepository, SubmodelRepository submodelRepository, ConceptDescriptionRepository conceptDescriptionRepository, + public PreconfigurationLoaderInitializer(AasEnvironmentLoader environmentLoader, AasEnvironmentPreconfigurationLoader preconfigurationLoader) { super(); - this.aasRepository = aasRepository; - this.submodelRepository = submodelRepository; - this.conceptDescriptionRepository = conceptDescriptionRepository; + this.environmentLoader = environmentLoader; this.preconfigurationLoader = preconfigurationLoader; } @@ -73,7 +65,7 @@ private void loadPreconfiguredEnvironment() throws IOException, InvalidFormatExc return; } - preconfigurationLoader.loadPreconfiguredEnvironments(aasRepository, submodelRepository, conceptDescriptionRepository); + preconfigurationLoader.loadPreconfiguredEnvironments(environmentLoader); } } 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 new file mode 100644 index 000000000..20b3d13d8 --- /dev/null +++ b/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/AasEnvironmentLoaderTest.java @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (C) 2024 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasenvironment; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.DeserializationException; +import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.AasEnvironmentLoader; +import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.CompleteEnvironment; +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.CrudAasRepository; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.CrudConceptDescriptionRepository; +import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.AasInMemoryBackendProvider; +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.exceptions.CollidingIdentifierException; +import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; +import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelInMemoryBackendProvider; +import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; +import org.eclipse.digitaltwin.basyx.submodelrepository.backend.CrudSubmodelRepository; +import org.eclipse.digitaltwin.basyx.submodelservice.InMemorySubmodelServiceFactory; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.core.io.DefaultResourceLoader; + + +/** + * Tests the behavior of {@link AasEnvironmentLoader} + * + * @author sonnenberg, mateusmolina + * + */ +public class AasEnvironmentLoaderTest { + + protected static final String TEST_ENVIRONMENT_JSON = "/org/eclipse/digitaltwin/basyx/aasenvironment/environment.json"; + protected static final String TEST_ENVIRONMENT_VERSION_ON_SECOND_JSON = "/org/eclipse/digitaltwin/basyx/aasenvironment/environment_version_on_second.json"; + protected static final String TEST_ENVIRONMENT_VERSION_AND_REVISION_ON_SECOND_JSON = "/org/eclipse/digitaltwin/basyx/aasenvironment/environment_version_and_revision_on_second.json"; + + protected static final String TEST_ENVIRONMENT_SHELLS_ONLY_JSON = "/org/eclipse/digitaltwin/basyx/aasenvironment/environment_with_shells_only.json"; + protected static final String TEST_ENVIRONMENT_SUBMODELS_ONLY_JSON = "/org/eclipse/digitaltwin/basyx/aasenvironment/environment_with_submodels_only.json"; + + protected static final PaginationInfo ALL = new PaginationInfo(0, null); + + protected AasRepository aasRepository; + protected SubmodelRepository submodelRepository; + protected ConceptDescriptionRepository conceptDescriptionRepository; + protected DefaultResourceLoader rLoader = new DefaultResourceLoader(); + + @Before + public void setUp() { + submodelRepository = Mockito.spy(new CrudSubmodelRepository(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory())); + aasRepository = Mockito.spy(new CrudAasRepository(new AasInMemoryBackendProvider(), new InMemoryAasServiceFactory())); + conceptDescriptionRepository = Mockito.spy(new CrudConceptDescriptionRepository(new ConceptDescriptionInMemoryBackendProvider())); + } + + protected void loadRepositories(List pathsToLoad) throws IOException, DeserializationException, InvalidFormatException { + AasEnvironmentLoader envLoader = new AasEnvironmentLoader(aasRepository, submodelRepository, conceptDescriptionRepository); + for (String path: pathsToLoad) { + File file = rLoader.getResource(path).getFile(); + envLoader.loadEnvironment(CompleteEnvironment.fromFile(file)); + } + } + + @Test + public void testWithResourceFile_AllElementsAreDeployed() throws InvalidFormatException, IOException, DeserializationException { + loadRepositories(List.of(TEST_ENVIRONMENT_JSON)); + + Assert.assertEquals(2, aasRepository.getAllAas(ALL).getResult().size()); + Assert.assertEquals(2, submodelRepository.getAllSubmodels(ALL).getResult().size()); + Assert.assertEquals(2, conceptDescriptionRepository.getAllConceptDescriptions(ALL).getResult().size()); + } + + @Test + public void testDeployedTwiceNoVersion_AllDeployedButNotOverriden() throws InvalidFormatException, IOException, DeserializationException { + loadRepositories(List.of(TEST_ENVIRONMENT_JSON)); + loadRepositories(List.of(TEST_ENVIRONMENT_JSON)); + + Mockito.verify(aasRepository, Mockito.times(2)).createAas(Mockito.any()); + Mockito.verify(aasRepository, Mockito.times(0)).updateAas(Mockito.anyString(), Mockito.any()); + + Mockito.verify(submodelRepository, Mockito.times(2)).createSubmodel(Mockito.any()); + Mockito.verify(submodelRepository, Mockito.times(0)).updateSubmodel(Mockito.anyString(), Mockito.any()); + + Assert.assertEquals(2, aasRepository.getAllAas(ALL).getResult().size()); + Assert.assertEquals(2, submodelRepository.getAllSubmodels(ALL).getResult().size()); + Assert.assertEquals(2, conceptDescriptionRepository.getAllConceptDescriptions(ALL).getResult().size()); + } + + @Test + public void testDeployedTwiceWithSameVersion_AllDeployedButNotOverriden() throws InvalidFormatException, IOException, DeserializationException { + loadRepositories(List.of(TEST_ENVIRONMENT_VERSION_ON_SECOND_JSON)); + loadRepositories(List.of(TEST_ENVIRONMENT_VERSION_ON_SECOND_JSON)); + + Mockito.verify(aasRepository, Mockito.times(2)).createAas(Mockito.any()); + Mockito.verify(aasRepository, Mockito.times(0)).updateAas(Mockito.anyString(), Mockito.any()); + + Mockito.verify(submodelRepository, Mockito.times(2)).createSubmodel(Mockito.any()); + Mockito.verify(submodelRepository, Mockito.times(0)).updateSubmodel(Mockito.anyString(), Mockito.any()); + + Assert.assertEquals(2, aasRepository.getAllAas(ALL).getResult().size()); + Assert.assertEquals(2, submodelRepository.getAllSubmodels(ALL).getResult().size()); + Assert.assertEquals(2, conceptDescriptionRepository.getAllConceptDescriptions(ALL).getResult().size()); + } + + @Test + public void testDeployedTwiceNewRevision_ElementsAreOverriden() throws InvalidFormatException, IOException, DeserializationException { + loadRepositories(List.of(TEST_ENVIRONMENT_VERSION_ON_SECOND_JSON)); + loadRepositories(List.of(TEST_ENVIRONMENT_VERSION_AND_REVISION_ON_SECOND_JSON)); + + Mockito.verify(aasRepository, Mockito.times(2)).createAas(Mockito.any()); + Mockito.verify(aasRepository, Mockito.times(1)).updateAas(Mockito.anyString(), Mockito.any()); + + Mockito.verify(submodelRepository, Mockito.times(2)).createSubmodel(Mockito.any()); + Mockito.verify(submodelRepository, Mockito.times(1)).updateSubmodel(Mockito.anyString(), Mockito.any()); + + Assert.assertEquals(2, aasRepository.getAllAas(ALL).getResult().size()); + Assert.assertEquals(2, submodelRepository.getAllSubmodels(ALL).getResult().size()); + Assert.assertEquals(2, conceptDescriptionRepository.getAllConceptDescriptions(ALL).getResult().size()); + } + + @Test + public void testDuplicateSubmodelIdsInEnvironments_ExceptionIsThrown() throws InvalidFormatException, IOException, DeserializationException { + + String expectedMsg = new CollidingIdentifierException("aas1").getMessage(); + Assert.assertThrows(expectedMsg, CollidingIdentifierException.class, () -> loadRepositories(List.of(TEST_ENVIRONMENT_SHELLS_ONLY_JSON, TEST_ENVIRONMENT_SHELLS_ONLY_JSON))); + } + + @Test + public void testDuplicateShellIdsInEnvironments_ExceptionIsThrown() { + String expectedMsg = new CollidingIdentifierException("sm1").getMessage(); + Assert.assertThrows(expectedMsg, CollidingIdentifierException.class, () -> loadRepositories(List.of(TEST_ENVIRONMENT_SUBMODELS_ONLY_JSON, TEST_ENVIRONMENT_SUBMODELS_ONLY_JSON))); + } +} \ No newline at end of file diff --git a/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/PreconfigurationLoaderTextualResourceTest.java b/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/PreconfigurationLoaderTextualResourceTest.java index 6321f6a2c..500e1aef4 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/PreconfigurationLoaderTextualResourceTest.java +++ b/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/PreconfigurationLoaderTextualResourceTest.java @@ -29,55 +29,29 @@ import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.DeserializationException; -import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.SerializationException; +import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.AasEnvironmentLoader; import org.eclipse.digitaltwin.basyx.aasenvironment.preconfiguration.AasEnvironmentPreconfigurationLoader; -import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.SimpleAasRepositoryFactory; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.SimpleConceptDescriptionRepositoryFactory; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.AasInMemoryBackendProvider; -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.exceptions.CollidingIdentifierException; -import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; -import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelInMemoryBackendProvider; -import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; -import org.eclipse.digitaltwin.basyx.submodelrepository.backend.SimpleSubmodelRepositoryFactory; -import org.eclipse.digitaltwin.basyx.submodelservice.InMemorySubmodelServiceFactory; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; -import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.ResourceLoader; -public class PreconfigurationLoaderTextualResourceTest { - - private static final String TEST_ENVIRONMENT_JSON = "/org/eclipse/digitaltwin/basyx/aasenvironment/environment.json"; - private static final String TEST_ENVIRONMENT_VERSION_ON_SECOND_JSON = "/org/eclipse/digitaltwin/basyx/aasenvironment/environment_version_on_second.json"; - private static final String TEST_ENVIRONMENT_VERSION_AND_REVISION_ON_SECOND_JSON = "/org/eclipse/digitaltwin/basyx/aasenvironment/environment_version_and_revision_on_second.json"; - - private static final String TEST_ENVIRONMENT_SHELLS_ONLY_JSON = "/org/eclipse/digitaltwin/basyx/aasenvironment/environment_with_shells_only.json"; - private static final String TEST_ENVIRONMENT_SUBMODELS_ONLY_JSON = "/org/eclipse/digitaltwin/basyx/aasenvironment/environment_with_submodels_only.json"; - - private static final PaginationInfo ALL = new PaginationInfo(0, null); - - private AasRepository aasRepository; - private SubmodelRepository submodelRepository; - private ConceptDescriptionRepository conceptDescriptionRepository; - private ResourceLoader rLoader = new DefaultResourceLoader(); - - @Before - public void setUp() { - submodelRepository = Mockito.spy(new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory()).create()); - aasRepository = Mockito.spy(new SimpleAasRepositoryFactory(new AasInMemoryBackendProvider(), new InMemoryAasServiceFactory()).create()); - conceptDescriptionRepository = Mockito.spy(new SimpleConceptDescriptionRepositoryFactory(new ConceptDescriptionInMemoryBackendProvider(), "cdRepo").create()); +/** + * Tests the behavior of {@link AasEnvironmentPreconfigurationLoader} + * + * @author sonnenberg, mateusmolina + * + */ +public class PreconfigurationLoaderTextualResourceTest extends AasEnvironmentLoaderTest { + + @Override + protected void loadRepositories(List pathsToLoad) throws IOException, InvalidFormatException, DeserializationException { + AasEnvironmentPreconfigurationLoader envLoader = new AasEnvironmentPreconfigurationLoader(rLoader, pathsToLoad); + envLoader.loadPreconfiguredEnvironments(new AasEnvironmentLoader(aasRepository, submodelRepository, conceptDescriptionRepository)); } @Test public void testWithEmptyResource_NoElementsAreDeployed() throws InvalidFormatException, IOException, DeserializationException { - AasEnvironmentPreconfigurationLoader envLoader = new AasEnvironmentPreconfigurationLoader(rLoader, List.of()); - envLoader.loadPreconfiguredEnvironments(aasRepository, submodelRepository, conceptDescriptionRepository); + loadRepositories(List.of()); Assert.assertTrue(aasRepository.getAllAas(ALL).getResult().isEmpty()); Assert.assertTrue(submodelRepository.getAllSubmodels(ALL).getResult().isEmpty()); Assert.assertTrue(conceptDescriptionRepository.getAllConceptDescriptions(ALL).getResult().isEmpty()); @@ -88,81 +62,4 @@ public void testWithEmptyResource_NoElementsAreDeployed() throws InvalidFormatEx Mockito.verify(submodelRepository, Mockito.never()).createSubmodel(Mockito.any()); Mockito.verify(submodelRepository, Mockito.never()).createSubmodel(Mockito.any()); } - - @Test - public void testWithResourceFile_AllElementsAreDeployed() throws InvalidFormatException, IOException, DeserializationException, SerializationException { - AasEnvironmentPreconfigurationLoader envLoader = new AasEnvironmentPreconfigurationLoader(rLoader, List.of(TEST_ENVIRONMENT_JSON)); - envLoader.loadPreconfiguredEnvironments(aasRepository, submodelRepository, conceptDescriptionRepository); - Assert.assertEquals(2, aasRepository.getAllAas(ALL).getResult().size()); - Assert.assertEquals(2, submodelRepository.getAllSubmodels(ALL).getResult().size()); - Assert.assertEquals(2, conceptDescriptionRepository.getAllConceptDescriptions(ALL).getResult().size()); - } - - @Test - public void testDeployedTwiceNoVersion_AllDeployedButNotOverriden() throws InvalidFormatException, IOException, DeserializationException, SerializationException { - AasEnvironmentPreconfigurationLoader envLoader = new AasEnvironmentPreconfigurationLoader(rLoader, List.of(TEST_ENVIRONMENT_JSON)); - - envLoader.loadPreconfiguredEnvironments(aasRepository, submodelRepository, conceptDescriptionRepository); - envLoader.loadPreconfiguredEnvironments(aasRepository, submodelRepository, conceptDescriptionRepository); - - Mockito.verify(aasRepository, Mockito.times(2)).createAas(Mockito.any()); - Mockito.verify(aasRepository, Mockito.times(0)).updateAas(Mockito.anyString(), Mockito.any()); - - Mockito.verify(submodelRepository, Mockito.times(2)).createSubmodel(Mockito.any()); - Mockito.verify(submodelRepository, Mockito.times(0)).updateSubmodel(Mockito.anyString(), Mockito.any()); - - Assert.assertEquals(2, aasRepository.getAllAas(ALL).getResult().size()); - Assert.assertEquals(2, submodelRepository.getAllSubmodels(ALL).getResult().size()); - Assert.assertEquals(2, conceptDescriptionRepository.getAllConceptDescriptions(ALL).getResult().size()); - } - - @Test - public void testDeployedTwiceWithSameVersion_AllDeployedButNotOverriden() throws InvalidFormatException, IOException, DeserializationException, SerializationException { - AasEnvironmentPreconfigurationLoader envLoader = new AasEnvironmentPreconfigurationLoader(rLoader, List.of(TEST_ENVIRONMENT_VERSION_ON_SECOND_JSON)); - - envLoader.loadPreconfiguredEnvironments(aasRepository, submodelRepository, conceptDescriptionRepository); - envLoader.loadPreconfiguredEnvironments(aasRepository, submodelRepository, conceptDescriptionRepository); - - Mockito.verify(aasRepository, Mockito.times(2)).createAas(Mockito.any()); - Mockito.verify(aasRepository, Mockito.times(0)).updateAas(Mockito.anyString(), Mockito.any()); - - Mockito.verify(submodelRepository, Mockito.times(2)).createSubmodel(Mockito.any()); - Mockito.verify(submodelRepository, Mockito.times(0)).updateSubmodel(Mockito.anyString(), Mockito.any()); - - Assert.assertEquals(2, aasRepository.getAllAas(ALL).getResult().size()); - Assert.assertEquals(2, submodelRepository.getAllSubmodels(ALL).getResult().size()); - Assert.assertEquals(2, conceptDescriptionRepository.getAllConceptDescriptions(ALL).getResult().size()); - } - - @Test - public void testDeployedTwiceNewRevision_ElementsAreOverriden() throws InvalidFormatException, IOException, DeserializationException, SerializationException { - AasEnvironmentPreconfigurationLoader envLoader = new AasEnvironmentPreconfigurationLoader(rLoader, List.of(TEST_ENVIRONMENT_VERSION_ON_SECOND_JSON)); - envLoader.loadPreconfiguredEnvironments(aasRepository, submodelRepository, conceptDescriptionRepository); - envLoader = new AasEnvironmentPreconfigurationLoader(rLoader, List.of(TEST_ENVIRONMENT_VERSION_AND_REVISION_ON_SECOND_JSON)); - envLoader.loadPreconfiguredEnvironments(aasRepository, submodelRepository, conceptDescriptionRepository); - - Mockito.verify(aasRepository, Mockito.times(2)).createAas(Mockito.any()); - Mockito.verify(aasRepository, Mockito.times(1)).updateAas(Mockito.anyString(), Mockito.any()); - - Mockito.verify(submodelRepository, Mockito.times(2)).createSubmodel(Mockito.any()); - Mockito.verify(submodelRepository, Mockito.times(1)).updateSubmodel(Mockito.anyString(), Mockito.any()); - - Assert.assertEquals(2, aasRepository.getAllAas(ALL).getResult().size()); - Assert.assertEquals(2, submodelRepository.getAllSubmodels(ALL).getResult().size()); - Assert.assertEquals(2, conceptDescriptionRepository.getAllConceptDescriptions(ALL).getResult().size()); - } - - @Test - public void testDuplicateSubmodelIdsInEnvironments_ExceptionIsThrown() throws InvalidFormatException, IOException, DeserializationException { - AasEnvironmentPreconfigurationLoader envLoader = new AasEnvironmentPreconfigurationLoader(rLoader, List.of(TEST_ENVIRONMENT_SHELLS_ONLY_JSON, TEST_ENVIRONMENT_SHELLS_ONLY_JSON)); - String expectedMsg = new CollidingIdentifierException("aas1").getMessage(); - Assert.assertThrows(expectedMsg, CollidingIdentifierException.class, () -> envLoader.loadPreconfiguredEnvironments(aasRepository, submodelRepository, conceptDescriptionRepository)); - } - - @Test - public void testDuplicateShellIdsInEnvironments_ExceptionIsThrown() { - AasEnvironmentPreconfigurationLoader envLoader = new AasEnvironmentPreconfigurationLoader(rLoader, List.of(TEST_ENVIRONMENT_SUBMODELS_ONLY_JSON, TEST_ENVIRONMENT_SUBMODELS_ONLY_JSON)); - String expectedMsg = new CollidingIdentifierException("sm1").getMessage(); - Assert.assertThrows(expectedMsg, CollidingIdentifierException.class, () -> envLoader.loadPreconfiguredEnvironments(aasRepository, submodelRepository, conceptDescriptionRepository)); - } } diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/DummyAasEnvironmentConfiguration.java b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/DummyAasEnvironmentConfiguration.java index 1d6fc0282..74a5fa576 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/DummyAasEnvironmentConfiguration.java +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/DummyAasEnvironmentConfiguration.java @@ -29,8 +29,12 @@ import org.eclipse.digitaltwin.basyx.aasenvironment.AasEnvironment; import org.eclipse.digitaltwin.basyx.aasenvironment.AasEnvironmentFactory; +import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.AasEnvironmentLoader; import org.eclipse.digitaltwin.basyx.aasenvironment.feature.AasEnvironmentFeature; import org.eclipse.digitaltwin.basyx.aasenvironment.feature.DecoratedAasEnvironmentFactory; +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; +import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionRepository; +import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -49,5 +53,11 @@ public class DummyAasEnvironmentConfiguration { public static AasEnvironment getAasEnvironment(AasEnvironmentFactory aasEnvironmentFactory, List features) { return new DecoratedAasEnvironmentFactory(aasEnvironmentFactory, features).create(); } - + + @Bean + @ConditionalOnMissingBean + public AasEnvironmentLoader createAasEnvironmentLoader(AasRepository aasRepository, SubmodelRepository submodelRepository, ConceptDescriptionRepository conceptDescriptionRepository) { + return new AasEnvironmentLoader(aasRepository, submodelRepository, conceptDescriptionRepository); + } + } diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/TestAuthorizedAasEnvironment.java b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/TestAuthorizedAasEnvironment.java index 586898f23..37053afec 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/TestAuthorizedAasEnvironment.java +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/TestAuthorizedAasEnvironment.java @@ -31,11 +31,11 @@ import java.io.IOException; import java.security.interfaces.RSAPublicKey; import java.util.Collection; + import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.message.BasicHeader; -import org.eclipse.basyx.digitaltwin.aasenvironment.http.DummyAASEnvironmentComponent; import org.eclipse.basyx.digitaltwin.aasenvironment.http.TestAasEnvironmentHTTP; import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; import org.eclipse.digitaltwin.aas4j.v3.model.ConceptDescription; @@ -71,139 +71,139 @@ public class TestAuthorizedAasEnvironment { private static AccessTokenProvider tokenProvider; private static ConfigurableApplicationContext appContext; private static String healthEndpointUrl = "http://127.0.0.1:8081/actuator/health"; - + @BeforeClass public static void setUp() throws FileNotFoundException, IOException { tokenProvider = new AccessTokenProvider(authenticaltionServerTokenEndpoint, clientId); - - appContext = new SpringApplication(DummyAASEnvironmentComponent.class).run(new String[] {}); - + + appContext = new SpringApplication(DummyAasEnvironmentComponent.class).run(new String[] {}); + addDummyElementsToRepositories(); } - + @Test public void healthEndpointWithoutAuthorization() throws IOException, ParseException { String expectedHealthEndpointOutput = getStringFromFile("authorization/HealthOutput.json"); - + CloseableHttpResponse healthCheckResponse = BaSyxHttpTestUtils.executeGetOnURL(healthEndpointUrl); assertEquals(HttpStatus.OK.value(), healthCheckResponse.getCode()); - + BaSyxHttpTestUtils.assertSameJSONContent(expectedHealthEndpointOutput, BaSyxHttpTestUtils.getResponseAsString(healthCheckResponse)); } @Test public void createSerializationWithCorrectRoleAndPermission() throws IOException { DummyCredential dummyCredential = DummyCredentialStore.BASYX_READER_CREDENTIAL; - + boolean includeConceptDescription = true; - + String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); - - CloseableHttpResponse retrievalResponse = getElementWithAuthorization(TestAasEnvironmentHTTP.createSerializationURL(includeConceptDescription), accessToken, new BasicHeader("Accept", TestAasEnvironmentHTTP.ACCEPT_JSON)); + + CloseableHttpResponse retrievalResponse = getElementWithAuthorization(TestAasEnvironmentHTTP.createSerializationURL(includeConceptDescription), accessToken, new BasicHeader("Accept", TestAasEnvironmentHTTP.JSON_MIMETYPE)); assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode()); } - + @Test public void createSerializationWithCorrectRoleAndSpecificSerializationPermission() throws IOException { DummyCredential dummyCredential = DummyCredentialStore.BASYX_READER_TWO_CREDENTIAL; - + boolean includeConceptDescription = true; - + String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); - - CloseableHttpResponse retrievalResponse = getElementWithAuthorization(TestAasEnvironmentHTTP.createSerializationURL(includeConceptDescription), accessToken, new BasicHeader("Accept", TestAasEnvironmentHTTP.ACCEPT_AASX)); + + CloseableHttpResponse retrievalResponse = getElementWithAuthorization(TestAasEnvironmentHTTP.createSerializationURL(includeConceptDescription), accessToken, new BasicHeader("Accept", TestAasEnvironmentHTTP.AASX_MIMETYPE)); assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode()); } - + @Test public void createSerializationWithCorrectRoleAndUnauthorizedSpecificSerializationPermission() throws IOException { DummyCredential dummyCredential = DummyCredentialStore.BASYX_READER_TWO_CREDENTIAL; - + boolean includeConceptDescription = true; - + String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); - - CloseableHttpResponse retrievalResponse = getElementWithAuthorization(TestAasEnvironmentHTTP.createSerializationURL(includeConceptDescription), accessToken, new BasicHeader("Accept", TestAasEnvironmentHTTP.ACCEPT_JSON)); + + CloseableHttpResponse retrievalResponse = getElementWithAuthorization(TestAasEnvironmentHTTP.createSerializationURL(includeConceptDescription), accessToken, new BasicHeader("Accept", TestAasEnvironmentHTTP.JSON_MIMETYPE)); assertEquals(HttpStatus.FORBIDDEN.value(), retrievalResponse.getCode()); } - + @Test public void createSerializationWithInsufficientPermissionRole() throws IOException { DummyCredential dummyCredential = DummyCredentialStore.BASYX_UPDATER_CREDENTIAL; - + boolean includeConceptDescription = true; - + String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); - - CloseableHttpResponse retrievalResponse = getElementWithAuthorization(TestAasEnvironmentHTTP.createSerializationURL(includeConceptDescription), accessToken, new BasicHeader("Accept", TestAasEnvironmentHTTP.ACCEPT_XML)); + + CloseableHttpResponse retrievalResponse = getElementWithAuthorization(TestAasEnvironmentHTTP.createSerializationURL(includeConceptDescription), accessToken, new BasicHeader("Accept", TestAasEnvironmentHTTP.XML_MIMETYPE)); assertEquals(HttpStatus.FORBIDDEN.value(), retrievalResponse.getCode()); } - + @Test public void createSerializationWithNoAuthorization() throws IOException { boolean includeConceptDescription = true; - - CloseableHttpResponse retrievalResponse = getElementWithNoAuthorization(TestAasEnvironmentHTTP.createSerializationURL(includeConceptDescription), new BasicHeader("Accept", TestAasEnvironmentHTTP.ACCEPT_XML)); + + CloseableHttpResponse retrievalResponse = getElementWithNoAuthorization(TestAasEnvironmentHTTP.createSerializationURL(includeConceptDescription), new BasicHeader("Accept", TestAasEnvironmentHTTP.XML_MIMETYPE)); assertEquals(HttpStatus.UNAUTHORIZED.value(), retrievalResponse.getCode()); } - + @Test public void createSerializationWithCorrectRoleAndSpecificTargetPermission() throws IOException { DummyCredential dummyCredential = DummyCredentialStore.BASYX_READER_SERIALIZATION_CREDENTIAL; - + boolean includeConceptDescription = true; - + String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); - - CloseableHttpResponse retrievalResponse = getElementWithAuthorization(TestAasEnvironmentHTTP.createSerializationURL(includeConceptDescription), accessToken, new BasicHeader("Accept", TestAasEnvironmentHTTP.ACCEPT_AASX)); + + CloseableHttpResponse retrievalResponse = getElementWithAuthorization(TestAasEnvironmentHTTP.createSerializationURL(includeConceptDescription), accessToken, new BasicHeader("Accept", TestAasEnvironmentHTTP.AASX_MIMETYPE)); assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode()); } - + @Test public void createSerializationWithCorrectRoleAndInsufficientTargetPermission() throws IOException { DummyCredential dummyCredential = DummyCredentialStore.BASYX_READER_SERIALIZATION_CREDENTIAL_TWO; - + boolean includeConceptDescription = true; - + String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); - - CloseableHttpResponse retrievalResponse = getElementWithAuthorization(TestAasEnvironmentHTTP.createSerializationURL(includeConceptDescription), accessToken, new BasicHeader("Accept", TestAasEnvironmentHTTP.ACCEPT_JSON)); + + CloseableHttpResponse retrievalResponse = getElementWithAuthorization(TestAasEnvironmentHTTP.createSerializationURL(includeConceptDescription), accessToken, new BasicHeader("Accept", TestAasEnvironmentHTTP.JSON_MIMETYPE)); assertEquals(HttpStatus.FORBIDDEN.value(), retrievalResponse.getCode()); } - + private static void addDummyElementsToRepositories() throws FileNotFoundException, IOException { String adminToken = getAdminAccessToken(); - + String modulus = getStringFromFile("authorization/modulus.txt"); String exponent = "AQAB"; - - RSAPublicKey rsaPublicKey = PublicKeyUtils.buildPublicKey(modulus, exponent); - - Jwt jwt = JwtTokenDecoder.decodeJwt(adminToken, rsaPublicKey); - + + RSAPublicKey rsaPublicKey = PublicKeyUtils.buildPublicKey(modulus, exponent); + + Jwt jwt = JwtTokenDecoder.decodeJwt(adminToken, rsaPublicKey); + SecurityContextHolder.getContext().setAuthentication(new JwtAuthenticationToken(jwt)); - + createDummyShellsOnRepository(TestAASEnvironmentSerialization.createDummyShells(), appContext.getBean(AasRepository.class)); createDummySubmodelsOnRepository(TestAASEnvironmentSerialization.createDummySubmodels(), appContext.getBean(SubmodelRepository.class)); createDummyConceptDescriptionsOnRepository(TestAASEnvironmentSerialization.createDummyConceptDescriptions(), appContext.getBean(ConceptDescriptionRepository.class)); - + SecurityContextHolder.clearContext(); } - + protected CloseableHttpResponse getElementWithAuthorization(String url, String accessToken, Header header) throws IOException { return BaSyxHttpTestUtils.executeAuthorizedGetOnURL(url, accessToken, header); } - + protected CloseableHttpResponse getElementWithNoAuthorization(String url, Header header) throws IOException { return BaSyxHttpTestUtils.executeGetOnURL(url, header); } private static String getAdminAccessToken() { DummyCredential dummyCredential = DummyCredentialStore.ADMIN_CREDENTIAL; - + return tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); } - + private static void createDummyConceptDescriptionsOnRepository(Collection conceptDescriptions, ConceptDescriptionRepository conceptDescriptionRepository) { conceptDescriptions.stream().forEach(conceptDescriptionRepository::createConceptDescription); } @@ -215,9 +215,9 @@ private static void createDummySubmodelsOnRepository(Collection submod private static void createDummyShellsOnRepository(Collection shells, AasRepository aasRepository) { shells.stream().forEach(aasRepository::createAas); } - + private static String getStringFromFile(String fileName) throws FileNotFoundException, IOException { return BaSyxHttpTestUtils.readJSONStringFromClasspath(fileName); } - + } diff --git a/basyx.aasenvironment/basyx.aasenvironment-http/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/http/AASEnvironmentHTTPApi.java b/basyx.aasenvironment/basyx.aasenvironment-http/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/http/AASEnvironmentHTTPApi.java index d8e1da841..07dac5d11 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-http/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/http/AASEnvironmentHTTPApi.java +++ b/basyx.aasenvironment/basyx.aasenvironment-http/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/http/AASEnvironmentHTTPApi.java @@ -34,6 +34,7 @@ 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.multipart.MultipartFile; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -69,4 +70,11 @@ ResponseEntity generateSerializationByIds( @Parameter(in = ParameterIn.QUERY, description = "The Submodels' unique ids (UTF8-BASE64-URL-encoded)", schema = @Schema()) @Valid @RequestParam(value = "submodelIds", required = false) List submodelIds, @Parameter(in = ParameterIn.QUERY, description = "Include Concept Descriptions?", schema = @Schema(defaultValue = "true")) @Valid @RequestParam(value = "includeConceptDescriptions", required = false, defaultValue = "true") Boolean includeConceptDescriptions); + + @Operation(summary = "Upload an environment file (XML, JSON, AASX)", description = "Uploads an environment file for processing.", tags = { "Environment API" }) + @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Environment successfully processed", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Boolean.class))), + @ApiResponse(responseCode = "400", description = "Bad Request, invalid file format or structure", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Boolean.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error, processing failure", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Boolean.class))) }) + @RequestMapping(value = "/upload", method = RequestMethod.POST) + ResponseEntity uploadEnvironment(@Parameter(description = "An environment file (XML, JSON, AASX)") @Valid @RequestParam("file") MultipartFile envFile); } diff --git a/basyx.aasenvironment/basyx.aasenvironment-http/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/http/AasEnvironmentApiHTTPController.java b/basyx.aasenvironment/basyx.aasenvironment-http/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/http/AasEnvironmentApiHTTPController.java index c8a26cbf3..a46f23205 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-http/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/http/AasEnvironmentApiHTTPController.java +++ b/basyx.aasenvironment/basyx.aasenvironment-http/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/http/AasEnvironmentApiHTTPController.java @@ -29,8 +29,13 @@ import java.util.ArrayList; import java.util.List; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.DeserializationException; import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.SerializationException; import org.eclipse.digitaltwin.basyx.aasenvironment.AasEnvironment; +import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.AasEnvironmentLoader; +import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.CompleteEnvironment; +import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.CompleteEnvironment.EnvironmentType; import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; import org.springframework.beans.factory.annotation.Autowired; @@ -40,6 +45,7 @@ import org.springframework.http.ResponseEntity; 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; @@ -59,10 +65,13 @@ public class AasEnvironmentApiHTTPController implements AASEnvironmentHTTPApi { private final AasEnvironment aasEnvironment; + private final AasEnvironmentLoader aasEnvironmentLoader; + @Autowired - public AasEnvironmentApiHTTPController(HttpServletRequest request, AasEnvironment aasEnvironment) { + public AasEnvironmentApiHTTPController(HttpServletRequest request, AasEnvironment aasEnvironment, AasEnvironmentLoader aasEnvironmentLoader) { this.request = request; this.aasEnvironment = aasEnvironment; + this.aasEnvironmentLoader = aasEnvironmentLoader; } @Override @@ -96,6 +105,24 @@ public ResponseEntity generateSerializationByIds( } } + @Override + public ResponseEntity uploadEnvironment(MultipartFile envFile) { + try { + EnvironmentType envType = EnvironmentType.getFromMimeType(envFile.getContentType()); + + if (envType == null) + envType = EnvironmentType.AASX; + + aasEnvironmentLoader.loadEnvironment(CompleteEnvironment.fromInputStream(envFile.getInputStream(), envType)); + + } catch (InvalidFormatException e) { + return new ResponseEntity(false, HttpStatus.BAD_REQUEST); + } catch (DeserializationException | IOException e) { + return new ResponseEntity(false, HttpStatus.INTERNAL_SERVER_ERROR); + } + return new ResponseEntity(true, HttpStatus.OK); + } + private List getOriginalIds(List ids) { List results = new ArrayList<>(); ids.forEach(id -> { diff --git a/basyx.aasenvironment/basyx.aasenvironment-http/src/test/java/org/eclipse/basyx/digitaltwin/aasenvironment/http/DummyAASEnvironmentComponent.java b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/java/org/eclipse/basyx/digitaltwin/aasenvironment/http/DummyAASEnvironmentComponent.java index 0f8859cb1..d01333dbc 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-http/src/test/java/org/eclipse/basyx/digitaltwin/aasenvironment/http/DummyAASEnvironmentComponent.java +++ b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/java/org/eclipse/basyx/digitaltwin/aasenvironment/http/DummyAASEnvironmentComponent.java @@ -30,26 +30,20 @@ import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; import org.eclipse.digitaltwin.aas4j.v3.model.AssetKind; +import org.eclipse.digitaltwin.aas4j.v3.model.ConceptDescription; import org.eclipse.digitaltwin.aas4j.v3.model.Submodel; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultAssetAdministrationShell; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultAssetInformation; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultConceptDescription; import org.eclipse.digitaltwin.basyx.aasenvironment.AasEnvironment; -import org.eclipse.digitaltwin.basyx.aasenvironment.TestAASEnvironmentSerialization; import org.eclipse.digitaltwin.basyx.aasenvironment.base.DefaultAASEnvironment; +import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.AasEnvironmentLoader; import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.SimpleAasRepositoryFactory; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.SimpleConceptDescriptionRepositoryFactory; -import org.eclipse.digitaltwin.basyx.aasrepository.backend.inmemory.AasInMemoryBackendProvider; -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.submodelrepository.SubmodelInMemoryBackendProvider; import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; -import org.eclipse.digitaltwin.basyx.submodelrepository.backend.SimpleSubmodelRepositoryFactory; import org.eclipse.digitaltwin.basyx.submodelservice.DummySubmodelFactory; -import org.eclipse.digitaltwin.basyx.submodelservice.InMemorySubmodelServiceFactory; +import org.eclipse.digitaltwin.basyx.submodelservice.SubmodelServiceHelper; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @SpringBootApplication(scanBasePackages = "org.eclipse.digitaltwin.basyx") @@ -61,21 +55,20 @@ public class DummyAASEnvironmentComponent { public static final String CONCEPT_DESCRIPTION_ID_NOT_INCLUDED_IN_ENV = "IdNotToBeIncludedInSerializedEnv"; @Bean - @ConditionalOnMissingBean - public AasEnvironment createAasEnvironment() { - SubmodelRepository submodelRepository = new SimpleSubmodelRepositoryFactory(new SubmodelInMemoryBackendProvider(), new InMemorySubmodelServiceFactory()).create(); - AasRepository aasRepository = new SimpleAasRepositoryFactory(new AasInMemoryBackendProvider(), new InMemoryAasServiceFactory()).create(); - ConceptDescriptionRepository conceptDescriptionRepository = new SimpleConceptDescriptionRepositoryFactory(new ConceptDescriptionInMemoryBackendProvider(), TestAASEnvironmentSerialization.createDummyConceptDescriptions()).create(); - - for (Submodel submodel : createDummySubmodels()) { - submodelRepository.createSubmodel(submodel); - } + public AasEnvironment createAasEnvironmentSerialization(AasRepository aasRepository, SubmodelRepository submodelRepository, ConceptDescriptionRepository conceptDescriptionRepository) { + initRepositories(aasRepository, submodelRepository, conceptDescriptionRepository); + return new DefaultAASEnvironment(aasRepository, submodelRepository, conceptDescriptionRepository); + } - for (AssetAdministrationShell shell : createDummyShells()) { - aasRepository.createAas(shell); - } + @Bean + public AasEnvironmentLoader createAasEnvironmentLoader(AasRepository aasRepository, SubmodelRepository submodelRepository, ConceptDescriptionRepository conceptDescriptionRepository) { + return new AasEnvironmentLoader(aasRepository, submodelRepository, conceptDescriptionRepository); + } - return new DefaultAASEnvironment(aasRepository, submodelRepository, conceptDescriptionRepository); + public void initRepositories(AasRepository aasRepository, SubmodelRepository submodelRepository, ConceptDescriptionRepository conceptDescriptionRepository) { + createDummySubmodels().forEach(submodelRepository::createSubmodel); + createDummyShells().forEach(aasRepository::createAas); + createDummyConceptDescriptions().forEach(conceptDescriptionRepository::createConceptDescription); } private Collection createDummySubmodels() { @@ -86,23 +79,25 @@ private Collection createDummySubmodels() { } private Collection createDummyShells() { - AssetAdministrationShell shell1 = new DefaultAssetAdministrationShell.Builder().id(AAS_TECHNICAL_DATA_ID) - .idShort(AAS_TECHNICAL_DATA_ID) - .assetInformation(new DefaultAssetInformation.Builder().assetKind(AssetKind.INSTANCE) - .globalAssetId(SUBMODEL_TECHNICAL_DATA_ID) - .build()) - .build(); + AssetAdministrationShell shell1 = new DefaultAssetAdministrationShell.Builder().id(AAS_TECHNICAL_DATA_ID).idShort(AAS_TECHNICAL_DATA_ID) + .assetInformation(new DefaultAssetInformation.Builder().assetKind(AssetKind.INSTANCE).globalAssetId(SUBMODEL_TECHNICAL_DATA_ID).build()).build(); - AssetAdministrationShell shell2 = new DefaultAssetAdministrationShell.Builder().id(AAS_OPERATIONAL_DATA_ID) - .idShort(AAS_OPERATIONAL_DATA_ID) - .assetInformation(new DefaultAssetInformation.Builder().assetKind(AssetKind.INSTANCE) - .globalAssetId(AAS_OPERATIONAL_DATA_ID) - .build()) - .build(); + AssetAdministrationShell shell2 = new DefaultAssetAdministrationShell.Builder().id(AAS_OPERATIONAL_DATA_ID).idShort(AAS_OPERATIONAL_DATA_ID) + .assetInformation(new DefaultAssetInformation.Builder().assetKind(AssetKind.INSTANCE).globalAssetId(AAS_OPERATIONAL_DATA_ID).build()).build(); Collection shells = new ArrayList<>(); shells.add(shell1); shells.add(shell2); return shells; } + private static Collection createDummyConceptDescriptions() { + Collection conceptDescriptions = new ArrayList<>(); + + conceptDescriptions.add(new DefaultConceptDescription.Builder().id(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_SEMANTIC_ID_PROPERTY).build()); + conceptDescriptions.add(new DefaultConceptDescription.Builder().id(DummySubmodelFactory.SUBMODEL_OPERATIONAL_DATA_SEMANTIC_ID_PROPERTY).build()); + conceptDescriptions.add(new DefaultConceptDescription.Builder().id(CONCEPT_DESCRIPTION_ID_NOT_INCLUDED_IN_ENV).build()); + + return conceptDescriptions; + } + } diff --git a/basyx.aasenvironment/basyx.aasenvironment-http/src/test/java/org/eclipse/basyx/digitaltwin/aasenvironment/http/TestAasEnvironmentHTTP.java b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/java/org/eclipse/basyx/digitaltwin/aasenvironment/http/TestAasEnvironmentHTTP.java index fa3e780f5..d8dd36c89 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-http/src/test/java/org/eclipse/basyx/digitaltwin/aasenvironment/http/TestAasEnvironmentHTTP.java +++ b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/java/org/eclipse/basyx/digitaltwin/aasenvironment/http/TestAasEnvironmentHTTP.java @@ -26,6 +26,7 @@ package org.eclipse.basyx.digitaltwin.aasenvironment.http; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import java.io.File; import java.io.FileInputStream; @@ -39,6 +40,7 @@ import org.apache.commons.io.IOUtils; import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.classic.methods.HttpPost; 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; @@ -47,9 +49,12 @@ import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.DeserializationException; import org.eclipse.digitaltwin.basyx.aasenvironment.TestAASEnvironmentSerialization; +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; +import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionRepository; import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier; import org.eclipse.digitaltwin.basyx.http.HttpBaSyxHeader; import org.eclipse.digitaltwin.basyx.http.serialization.BaSyxHttpTestUtils; +import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -60,15 +65,26 @@ public class TestAasEnvironmentHTTP { - public static final String ACCEPT_JSON = "application/json"; - public static final String ACCEPT_XML = "application/xml"; - public static final String ACCEPT_AASX = "application/asset-administration-shell-package+xml"; + public static final String JSON_MIMETYPE = "application/json"; + public static final String XML_MIMETYPE = "application/xml"; + public static final String AASX_MIMETYPE = "application/asset-administration-shell-package+xml"; + + private static final String AASX_ENV_PATH = "testEnvironment.aasx"; + private static final String JSON_ENV_PATH = "testEnvironment.json"; + private static final String XML_ENV_PATH = "testEnvironment.xml"; + private static final String WRONGEXT_ENV_PATH = "testEnvironment.txt"; private static ConfigurableApplicationContext appContext; + private static SubmodelRepository submodelRepo; + private static AasRepository aasRepo; + private static ConceptDescriptionRepository conceptDescriptionRepo; @BeforeClass public static void startAasRepo() throws Exception { appContext = new SpringApplication(DummyAASEnvironmentComponent.class).run(new String[] {}); + submodelRepo = appContext.getBean(SubmodelRepository.class); + aasRepo = appContext.getBean(AasRepository.class); + conceptDescriptionRepo = appContext.getBean(ConceptDescriptionRepository.class); } @Test @@ -81,7 +97,7 @@ public void baSyxResponseHeader() throws IOException, ProtocolException { public void testAASEnvironmentSertializationWithJSON() throws IOException, ParseException, DeserializationException { boolean includeConceptDescription = true; - CloseableHttpResponse response = executeGetOnURL(createSerializationURL(includeConceptDescription), ACCEPT_JSON); + CloseableHttpResponse response = executeGetOnURL(createSerializationURL(includeConceptDescription), JSON_MIMETYPE); String actual = BaSyxHttpTestUtils.getResponseAsString(response); TestAASEnvironmentSerialization.validateJSON(actual, includeConceptDescription); } @@ -90,7 +106,7 @@ public void testAASEnvironmentSertializationWithJSON() throws IOException, Parse public void testAASEnvironmentSertializationWithXML() throws IOException, ParseException, DeserializationException { boolean includeConceptDescription = true; - CloseableHttpResponse response = executeGetOnURL(createSerializationURL(includeConceptDescription), ACCEPT_XML); + CloseableHttpResponse response = executeGetOnURL(createSerializationURL(includeConceptDescription), XML_MIMETYPE); String actual = BaSyxHttpTestUtils.getResponseAsString(response); TestAASEnvironmentSerialization.validateXml(actual, includeConceptDescription); } @@ -99,7 +115,7 @@ public void testAASEnvironmentSertializationWithXML() throws IOException, ParseE public void testAASEnvironmentSertializationWithAASX() throws IOException, ParseException, DeserializationException, InvalidFormatException { boolean includeConceptDescription = true; - CloseableHttpResponse response = executeGetOnURL(createSerializationURL(includeConceptDescription), ACCEPT_AASX); + CloseableHttpResponse response = executeGetOnURL(createSerializationURL(includeConceptDescription), AASX_MIMETYPE); assertEquals(HttpStatus.OK.value(), response.getCode()); TestAASEnvironmentSerialization.checkAASX(response.getEntity() @@ -110,7 +126,7 @@ public void testAASEnvironmentSertializationWithAASX() throws IOException, Parse public void testAASEnvironmentSertializationWithAASXExcludeCD() throws IOException, ParseException, DeserializationException, InvalidFormatException { boolean includeConceptDescription = false; - CloseableHttpResponse response = executeGetOnURL(createSerializationURL(includeConceptDescription), ACCEPT_AASX); + CloseableHttpResponse response = executeGetOnURL(createSerializationURL(includeConceptDescription), AASX_MIMETYPE); assertEquals(HttpStatus.OK.value(), response.getCode()); TestAASEnvironmentSerialization.checkAASX(response.getEntity() @@ -121,7 +137,7 @@ public void testAASEnvironmentSertializationWithAASXExcludeCD() throws IOExcepti public void testAASEnvironmentWithWrongParameter() throws IOException { boolean includeConceptDescription = true; - CloseableHttpResponse response = executeGetOnURL(getSerializationURL(new ArrayList(), new ArrayList(), includeConceptDescription), ACCEPT_JSON); + CloseableHttpResponse response = executeGetOnURL(getSerializationURL(new ArrayList(), new ArrayList(), includeConceptDescription), JSON_MIMETYPE); assertEquals(HttpStatus.BAD_REQUEST.value(), response.getCode()); } @@ -142,10 +158,50 @@ public void testAASEnvironmentWithWrongId() throws IOException { aasIds.add("wrongAasId"); submodelIds.add("wrongSubmodelId"); - CloseableHttpResponse response = executeGetOnURL(getSerializationURL(aasIds, submodelIds, includeConceptDescription), ACCEPT_JSON); + CloseableHttpResponse response = executeGetOnURL(getSerializationURL(aasIds, submodelIds, includeConceptDescription), JSON_MIMETYPE); assertEquals(HttpStatus.NOT_FOUND.value(), response.getCode()); } + @Test + public void testEnvironmentUpload_AASX() throws IOException, InvalidFormatException, UnsupportedOperationException, DeserializationException, ParseException { + CloseableHttpResponse response = BaSyxHttpTestUtils.executePostRequest(HttpClients.createDefault(), createPostRequestWithFile(AASX_ENV_PATH, AASX_MIMETYPE)); + + assertEquals(HttpStatus.OK.value(), response.getCode()); + + assertNotNull(aasRepo.getAas("http://customer.com/aas/9175_7013_7091_9168")); + assertNotNull(submodelRepo.getSubmodel("http://i40.customer.com/type/1/1/7A7104BDAB57E184")); + assertNotNull(conceptDescriptionRepo.getConceptDescription("http://www.vdi2770.com/blatt1/Entwurf/Okt18/cd/Description/Title")); + } + + @Test + public void testEnvironmentUpload_JSON() throws IOException, InvalidFormatException, UnsupportedOperationException, DeserializationException, ParseException { + CloseableHttpResponse response = BaSyxHttpTestUtils.executePostRequest(HttpClients.createDefault(), createPostRequestWithFile(JSON_ENV_PATH, JSON_MIMETYPE)); + + assertEquals(HttpStatus.OK.value(), response.getCode()); + + assertNotNull(aasRepo.getAas("https://acplt.test/Test_AssetAdministrationShell")); + assertNotNull(submodelRepo.getSubmodel("http://acplt.test/Submodels/Assets/TestAsset/Identification")); + assertNotNull(conceptDescriptionRepo.getConceptDescription("https://acplt.test/Test_ConceptDescription")); + } + + @Test + public void testEnvironmentUpload_XML() throws IOException, InvalidFormatException, UnsupportedOperationException, DeserializationException, ParseException { + CloseableHttpResponse response = BaSyxHttpTestUtils.executePostRequest(HttpClients.createDefault(), createPostRequestWithFile(XML_ENV_PATH, XML_MIMETYPE)); + + assertEquals(HttpStatus.OK.value(), response.getCode()); + + assertNotNull(aasRepo.getAas("http://customer.test/aas/9175_7013_7091_9168")); + assertNotNull(submodelRepo.getSubmodel("http://i40.customer.test/type/1/1/7A7104BDAB57E184")); + assertNotNull(conceptDescriptionRepo.getConceptDescription("http://www.vdi2770.test/blatt1/Entwurf/Okt18/cd/Description/Title")); + } + + @Test + public void testEnvironmentUpload_WrongExtension() throws IOException, InvalidFormatException, UnsupportedOperationException, DeserializationException, ParseException { + CloseableHttpResponse response = BaSyxHttpTestUtils.executePostRequest(HttpClients.createDefault(), createPostRequestWithFile(WRONGEXT_ENV_PATH, "text/plain")); + + assertEquals(HttpStatus.BAD_REQUEST.value(), response.getCode()); + } + public static String createSerializationURL(boolean includeConceptDescription) { return getSerializationURL(createIdCollection(DummyAASEnvironmentComponent.AAS_TECHNICAL_DATA_ID, DummyAASEnvironmentComponent.AAS_OPERATIONAL_DATA_ID), createIdCollection(DummyAASEnvironmentComponent.SUBMODEL_OPERATIONAL_DATA_ID, DummyAASEnvironmentComponent.SUBMODEL_TECHNICAL_DATA_ID), includeConceptDescription); @@ -163,10 +219,20 @@ private static HttpGet createGetRequestWithHeader(String url, String header) { return aasCreateRequest; } - private static String getURL() { + private static HttpPost createPostRequestWithFile(String filepath, String contentType) throws FileNotFoundException { + java.io.File file = ResourceUtils.getFile("classpath:" + filepath); + + return BaSyxHttpTestUtils.createPostRequestWithFile(getAASXUploadURL(), file, contentType); + } + + public static String getURL() { return "http://localhost:8081"; } + private static String getAASXUploadURL() { + return getURL() + "/upload"; + } + public static String getSerializationURL(Collection aasIds, Collection submodelIds, boolean includeConceptDescription) { String aasIdsArrayString = createIdsArrayString(aasIds); String submodelIdsArrayString = createIdsArrayString(submodelIds); @@ -191,7 +257,7 @@ public static List createIdCollection(String... ids) { } return results; } - + public String readStringFromFile(String fileName) throws FileNotFoundException, IOException { File file = ResourceUtils.getFile(fileName); InputStream in = new FileInputStream(file); @@ -202,5 +268,4 @@ public String readStringFromFile(String fileName) throws FileNotFoundException, public static void shutdown() { appContext.close(); } - } diff --git a/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/testEnvironment.aasx b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/testEnvironment.aasx new file mode 100644 index 000000000..9cd1b82d6 Binary files /dev/null and b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/testEnvironment.aasx differ diff --git a/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/testEnvironment.json b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/testEnvironment.json new file mode 100644 index 000000000..3e02f62f3 --- /dev/null +++ b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/testEnvironment.json @@ -0,0 +1,236 @@ +{ + "assetAdministrationShells": [ + { + "modelType": "AssetAdministrationShell", + "assetInformation": { + "assetKind": "Instance", + "globalAssetId": "https://acplt.test/Test_Asset" + }, + "derivedFrom": { + "keys": [ + { + "type": "AssetAdministrationShell", + "value": "https://acplt.test/TestAssetAdministrationShell" + } + ], + "type": "ExternalReference" + }, + "submodels": [ + { + "keys": [ + { + "type": "Submodel", + "value": "https://acplt.test/Test_Submodel" + } + ], + "type": "ExternalReference" + }, + { + "keys": [ + { + "type": "Submodel", + "value": "http://acplt.test/Submodels/Assets/TestAsset/BillOfMaterial" + } + ], + "type": "ExternalReference" + }, + { + "keys": [ + { + "type": "Submodel", + "value": "http://acplt.test/Submodels/Assets/TestAsset/Identification" + } + ], + "type": "ExternalReference" + } + ], + "administration": { + "revision": "9", + "version": "0" + }, + "id": "https://acplt.test/Test_AssetAdministrationShell", + "idShort": "TestAssetAdministrationShell", + "description": [ + { + "language": "en-us", + "text": "An Example Asset Administration Shell for the test application" + }, + { + "language": "de", + "text": "Ein Beispiel-Verwaltungsschale für eine Test-Anwendung" + } + ] + } + ], + "conceptDescriptions": [ + { + "modelType": "ConceptDescription", + "administration": { + "revision": "9", + "version": "0" + }, + "id": "https://acplt.test/Test_ConceptDescription", + "idShort": "TestConceptDescription", + "isCaseOf": [ + { + "keys": [ + { + "type": "GlobalReference", + "value": "http://acplt.test/DataSpecifications/Conceptdescription/TestConceptDescription" + } + ], + "type": "ExternalReference" + } + ], + "description": [ + { + "language": "en-us", + "text": "An example concept description for the test application" + }, + { + "language": "de", + "text": "Ein Beispiel-ConceptDescription für eine Test-Anwendung" + } + ] + } + ], + "submodels": [ + { + "modelType": "Submodel", + "kind": "Instance", + "semanticId": { + "keys": [ + { + "type": "Submodel", + "value": "http://acplt.test/SubmodelTemplates/AssetIdentification" + } + ], + "type": "ExternalReference" + }, + "administration": { + "revision": "9", + "version": "0" + }, + "id": "http://acplt.test/Submodels/Assets/TestAsset/Identification", + "idShort": "Identification", + "submodelElements": [ + { + "modelType": "Property", + "semanticId": { + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAO677#002" + } + ], + "type": "ExternalReference" + }, + "value": "http://acplt.test/ValueId/ACPLT", + "valueId": { + "keys": [ + { + "type": "GlobalReference", + "value": "http://acplt.test/ValueId/ACPLT" + } + ], + "type": "ExternalReference" + }, + "valueType": "xs:string", + "qualifiers": [ + { + "type": "http://acplt.test/Qualifier/ExampleQualifier", + "value": "100", + "valueType": "xs:int" + }, + { + "type": "http://acplt.test/Qualifier/ExampleQualifier2", + "value": "50", + "valueType": "xs:int" + } + ], + "idShort": "ManufacturerName", + "displayName": [ + { + "language": "en-us", + "text": "Manufacturer Name" + } + ], + "description": [ + { + "language": "en-us", + "text": "Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation." + }, + { + "language": "de", + "text": "Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist" + } + ] + }, + { + "modelType": "Property", + "category": "VARIABLE", + "semanticId": { + "keys": [ + { + "type": "GlobalReference", + "value": "http://opcfoundation.test/UA/DI/1.1/DeviceType/Serialnumber" + } + ], + "type": "ExternalReference" + }, + "supplementalSemanticIds": [ + { + "keys": [ + { + "type": "GlobalReference", + "value": "something_random_e14ad770" + } + ], + "type": "ExternalReference" + }, + { + "keys": [{ + "type": "GlobalReference", + "value": "something_random_bd061acd" + }], + "type": "ExternalReference" + } + + ], + "value": "978-8234-234-342", + "valueId": { + "keys": [ + { + "type": "GlobalReference", + "value": "978-8234-234-342" + } + ], + "type": "ExternalReference" + }, + "valueType": "xs:string", + "idShort": "InstanceId", + "description": [ + { + "language": "en-us", + "text": "Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation." + }, + { + "language": "de", + "text": "Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist" + } + ] + } + ], + "description": [ + { + "language": "en-us", + "text": "An example asset identification submodel for the test application" + }, + { + "language": "de", + "text": "Ein Beispiel-Identifikations-Submodel für eine Test-Anwendung" + } + ] + } + ] +} diff --git a/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/testEnvironment.txt b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/testEnvironment.txt new file mode 100644 index 000000000..e69de29bb diff --git a/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/testEnvironment.xml b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/testEnvironment.xml new file mode 100644 index 000000000..275ef0276 --- /dev/null +++ b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/testEnvironment.xml @@ -0,0 +1,450 @@ + + + + + ExampleMotor + http://customer.test/aas/9175_7013_7091_9168 + + Instance + http://customer.test/assets/KHBVZJSQKIY + + + EquipmentID + 538fd1b3-f99f-4a52-9c75-72e9fa921270 + + ExternalReference + + + GlobalReference + http://customer.test/Systems/ERP/012 + + + + + + DeviceID + QjYgPggjwkiHk4RrQiYSLg== + + ExternalReference + + + GlobalReference + http://customer.test/Systems/IoT/1 + + + + + + + file:///master/verwaltungsschale-detail-part1.png + image/png + + + + + ExternalReference + + + Submodel + http://i40.customer.test/type/1/1/7A7104BDAB57E184 + + + + + ExternalReference + + + Submodel + http://i40.customer.test/instance/1/1/AC69B1CB44F07935 + + + + + ExternalReference + + + Submodel + http://i40.customer.test/type/1/1/1A7B62B529F19152 + + + + + + + + + TechnicalData + http://i40.customer.test/type/1/1/7A7104BDAB57E184 + + ExternalReference + + + GlobalReference + 0173-1#01-AFZ615#016 + + + + + + PARAMETER + MaxRotationSpeed + + ExternalReference + + + ConceptDescription + 0173-1#02-BAA120#008 + + + + xs:integer + 5000 + + + + + Documentation + http://i40.customer.test/type/1/1/1A7B62B529F19152 + Instance + + + OperatingManual + + ExternalReference + + + ConceptDescription + http://www.vdi2770.test/blatt1/Entwurf/Okt18/cd/Document + + + + + + Title + + ExternalReference + + + ConceptDescription + http://www.vdi2770.test/blatt1/Entwurf/Okt18/cd/Description/Title + + + + xs:string + OperatingManual + + + DigitalFile_PDF + + ExternalReference + + + ConceptDescription + http://www.vdi2770.test/blatt1/Entwurf/Okt18/cd/StoredDocumentRepresentation/DigitalFile + + + + file:///aasx/OperatingManual.pdf + application/pdf + + + + + + + OperationalData + http://i40.customer.test/instance/1/1/AC69B1CB44F07935 + Instance + + + VARIABLE + RotationSpeed + + ExternalReference + + + ConceptDescription + http://customer.test/cd/1/1/18EBD56F6B43D895 + + + + xs:integer + 4370 + + + + + + + Title + http://www.vdi2770.test/blatt1/Entwurf/Okt18/cd/Description/Title + + + + ExternalReference + + + GlobalReference + https://admin-shell.io/aas/3/0/RC02/DataSpecificationIEC61360 + + + + + + + + EN + Title + + + DE + Titel + + + + + EN + Title + + + DE + Titel + + + ExampleString + ExampleString + STRING_TRANSLATABLE + + + EN + SprachabhängigerTiteldesDokuments. + + + + + + + + + DigitalFile + http://www.vdi2770.test/blatt1/Entwurf/Okt18/cd/StoredDocumentRepresentation/DigitalFile + + + + ExternalReference + + + GlobalReference + https://admin-shell.io/aas/3/0/RC02/DataSpecificationIEC61360 + + + + + + + + EN + DigitalFile + + + EN + DigitalFile + + + + + EN + DigitalFile + + + DE + DigitaleDatei + + + ExampleString + ExampleString + STRING + + + EN + A file representing the document version. In addition to the mandatory PDF file, other files can be specified. + + + + + + + + + PROPERTY + MaxRotationSpeed + + 2 + 1 + + 0173-1#02-BAA120#009 + + + + ExternalReference + + + GlobalReference + https://admin-shell.io/aas/3/0/RC02/DataSpecificationIEC61360 + + + + + + + + de + max.Drehzahl + + + en + Max.rotationspeed + + + 1/min + + ExternalReference + + + GlobalReference + 0173-1#05-AAA650#002 + + + + ExampleString + REAL_MEASURE + + + de + HöchstezulässigeDrehzahl,mitwelcherderMotoroderdieSpeiseinheitbetriebenwerdendarf + + + EN + Greatestpermissiblerotationspeedwithwhichthemotororfeedingunitmaybeoperated + + + + + + + + + PROPERTY + RotationSpeed + http://customer.test/cd/1/1/18EBD56F6B43D895 + + + + ExternalReference + + + GlobalReference + https://admin-shell.io/aas/3/0/RC02/DataSpecificationIEC61360 + + + + + + + + DE + AktuelleDrehzahl + + + EN + Actualrotationspeed + + + + + DE + AktuelleDrehzahl + + + EN + ActRotationSpeed + + + 1/min + + ExternalReference + + + GlobalReference + 0173-1#05-AAA650#002 + + + + ExampleString + REAL_MEASURE + + + DE + Aktuelle Drehzahl, mitwelcher der Motor oder die Speiseinheit betrieben wird + + + EN + Actual rotationspeed with which the motor or feedingunit is operated + + + + + + + + + Document + http://www.vdi2770.test/blatt1/Entwurf/Okt18/cd/Document + + + + ExternalReference + + + GlobalReference + https://admin-shell.io/aas/3/0/RC02/DataSpecificationIEC61360 + + + + + + + + EN + Document + + + + + EN + Document + + + DE + Dokument + + + ExampleString + [ISO15519-1:2010] + STRING + + + EN + Feste und geordnete Menge von für die Verwendung durch Personen bestimmte Informationen, die verwaltet und als Einheit zwischen Benutzern und System ausgetauscht werden kann. + + + + + + + + + + diff --git a/basyx.aasenvironment/basyx.aasenvironment.component/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/component/AasEnvironmentConfiguration.java b/basyx.aasenvironment/basyx.aasenvironment.component/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/component/AasEnvironmentConfiguration.java index eb7f9de4e..e2f2fd024 100644 --- a/basyx.aasenvironment/basyx.aasenvironment.component/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/component/AasEnvironmentConfiguration.java +++ b/basyx.aasenvironment/basyx.aasenvironment.component/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/component/AasEnvironmentConfiguration.java @@ -29,8 +29,12 @@ import org.eclipse.digitaltwin.basyx.aasenvironment.AasEnvironment; import org.eclipse.digitaltwin.basyx.aasenvironment.AasEnvironmentFactory; +import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.AasEnvironmentLoader; import org.eclipse.digitaltwin.basyx.aasenvironment.feature.AasEnvironmentFeature; import org.eclipse.digitaltwin.basyx.aasenvironment.feature.DecoratedAasEnvironmentFactory; +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; +import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionRepository; +import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -38,7 +42,7 @@ /** * Configuration for aas environment for dependency injection * - * @author zhangzai + * @author zhangzai, mateusmolina * */ @Configuration @@ -49,4 +53,9 @@ public class AasEnvironmentConfiguration { public static AasEnvironment getAasEnvironment(AasEnvironmentFactory aasEnvironmentFactory, List features) { return new DecoratedAasEnvironmentFactory(aasEnvironmentFactory, features).create(); } + + @Bean + public AasEnvironmentLoader createAasEnvironmentLoader(AasRepository aasRepository, SubmodelRepository submodelRepository, ConceptDescriptionRepository conceptDescriptionRepository) { + return new AasEnvironmentLoader(aasRepository, submodelRepository, conceptDescriptionRepository); + } } diff --git a/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/serialization/BaSyxHttpTestUtils.java b/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/serialization/BaSyxHttpTestUtils.java index 36898a30c..e65c4f78a 100644 --- a/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/serialization/BaSyxHttpTestUtils.java +++ b/basyx.common/basyx.http/src/test/java/org/eclipse/digitaltwin/basyx/http/serialization/BaSyxHttpTestUtils.java @@ -287,6 +287,15 @@ public static CloseableHttpResponse executePutRequest(CloseableHttpClient client return response; } + public static CloseableHttpResponse executePostRequest(CloseableHttpClient client, HttpPost postRequest) throws IOException { + CloseableHttpResponse response = client.execute(postRequest); + + HttpEntity responseEntity = response.getEntity(); + + EntityUtils.consume(responseEntity); + return response; + } + public static HttpPut createPutRequestWithFile(String url, String fileName, java.io.File file) { HttpPut putRequest = new HttpPut(url); @@ -367,6 +376,19 @@ private static HttpPost createPostRequest(String url, String content) { return aasCreateRequest; } + + public static HttpPost createPostRequestWithFile(String url, java.io.File file, String contentType) { + HttpPost postRequest = new HttpPost(url); + + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + + builder.addPart("file", new FileBody(file, ContentType.create(contentType))); + builder.setContentType(ContentType.MULTIPART_FORM_DATA); + + HttpEntity multipart = builder.build(); + postRequest.setEntity(multipart); + return postRequest; + } private static HttpPost createAuthorizedPostRequest(String url, String content, String accessToken) { HttpPost aasCreateRequest = createPostRequestWithAuthorizationHeader(url, accessToken);