From 932653813df2bb357b7def1ebeb622105b9d8fd7 Mon Sep 17 00:00:00 2001 From: FriedJannik Date: Wed, 28 Jan 2026 07:55:03 +0100 Subject: [PATCH 1/5] Fixes Bug in MongoDBStorage with Entity in Entities --- .../backend/MongoDbSubmodelOperations.java | 33 ++++++++++++++++++- .../backend/MongoFilterBuilder.java | 23 ++++++++++++- .../submodelservice/SubmodelServiceSuite.java | 19 +++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/basyx.submodelservice/basyx.submodelservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/MongoDbSubmodelOperations.java b/basyx.submodelservice/basyx.submodelservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/MongoDbSubmodelOperations.java index 92961100a..82fb683c6 100644 --- a/basyx.submodelservice/basyx.submodelservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/MongoDbSubmodelOperations.java +++ b/basyx.submodelservice/basyx.submodelservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/MongoDbSubmodelOperations.java @@ -193,7 +193,8 @@ public void createSubmodelElement(String submodelId, String idShortPath, Submode @Override public void updateSubmodelElement(String submodelId, String idShortPath, SubmodelElement submodelElement) throws ElementDoesNotExistException { - MongoFilterResult filterResult = MongoFilterBuilder.parse(idShortPath); + List parentElements = getParentElements(submodelId, idShortPath); + MongoFilterResult filterResult = MongoFilterBuilder.parse(idShortPath, parentElements); Query query = new Query(Criteria.where("_id").is(submodelId)); Update update = new Update().set(filterResult.key(), submodelElement); @@ -326,6 +327,36 @@ private boolean existsSubmodelElement(String submodelId, String idShortPath){ } } + /** + * Gets the list of parent SubmodelElements along the path. + * This is used to determine if any parent is an Entity (which uses 'statements' instead of 'value'). + */ + private List getParentElements(String submodelId, String idShortPath) { + List parents = new ArrayList<>(); + String[] segments = idShortPath.split("\\."); + + if (segments.length <= 1) { + return parents; + } + + StringBuilder currentPath = new StringBuilder(); + for (int i = 0; i < segments.length - 1; i++) { + if (currentPath.length() > 0) { + currentPath.append("."); + } + currentPath.append(segments[i]); + try { + SubmodelElement element = getSubmodelElement(submodelId, currentPath.toString()); + parents.add(element); + } catch (ElementDoesNotExistException e) { + // Element doesn't exist, stop here + break; + } + } + + return parents; + } + private static boolean hasLimit(PaginationInfo pInfo) { return pInfo.getLimit() != null && pInfo.getLimit() > 0; } diff --git a/basyx.submodelservice/basyx.submodelservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/MongoFilterBuilder.java b/basyx.submodelservice/basyx.submodelservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/MongoFilterBuilder.java index 80be137be..7acedfd53 100644 --- a/basyx.submodelservice/basyx.submodelservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/MongoFilterBuilder.java +++ b/basyx.submodelservice/basyx.submodelservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/MongoFilterBuilder.java @@ -29,6 +29,8 @@ import java.util.Deque; import java.util.List; +import org.eclipse.digitaltwin.aas4j.v3.model.Entity; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; import org.eclipse.digitaltwin.basyx.submodelservice.backend.IdShortPathParser.GenericPath; import org.eclipse.digitaltwin.basyx.submodelservice.backend.IdShortPathParser.IdShortPath; import org.eclipse.digitaltwin.basyx.submodelservice.backend.IdShortPathParser.IndexPath; @@ -54,6 +56,17 @@ public final class MongoFilterBuilder{ static final String KEY_ID_SHORT = "idShort"; public static MongoFilterResult parse(@NonNull String idShortPath) { + return parse(idShortPath, List.of()); + } + + /** + * Parses the idShortPath and builds MongoDB filter result. + * + * @param idShortPath the path to parse + * @param parentElements list of parent SubmodelElements along the path (used to determine if Entity uses 'statements') + * @return MongoFilterResult with the update key and filters + */ + public static MongoFilterResult parse(@NonNull String idShortPath, @NonNull List parentElements) { Deque paths = IdShortPathParser.parse(idShortPath); assert !paths.isEmpty(); @@ -61,6 +74,7 @@ public static MongoFilterResult parse(@NonNull String idShortPath) { StringBuilder updateKey = new StringBuilder(); List filterArray = new ArrayList<>(); int filterCounter = 0; + int parentIndex = 0; updateKey.append(KEY_SUBMODEL_ELEMENTS); @@ -74,7 +88,14 @@ public static MongoFilterResult parse(@NonNull String idShortPath) { while (!paths.isEmpty()) { GenericPath segment = paths.pop(); - updateKey.append(".").append(KEY_VALUE); + // Determine whether to use 'statements' (for Entity) or 'value' (for others) + String childKey = KEY_VALUE; + if (parentIndex < parentElements.size() && parentElements.get(parentIndex) instanceof Entity) { + childKey = KEY_STATEMENTS; + } + updateKey.append(".").append(childKey); + parentIndex++; + if (segment instanceof IdShortPath idPath) { placeholder = "elem" + filterCounter++; updateKey.append(".$[").append(placeholder).append("]"); diff --git a/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceSuite.java b/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceSuite.java index e8b4687c6..c513136d0 100644 --- a/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceSuite.java +++ b/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceSuite.java @@ -670,6 +670,25 @@ public void patchSubmodelElements() { assertEquals(submodelElementsPatch, patchedSubmodel.getSubmodelElements()); } + @Test + public void addEntityToEntity() { + List submodelElements = new ArrayList<>(); + Entity entity = new DefaultEntity.Builder().idShort("MainEntity").build(); + submodelElements.add(entity); + Submodel submodel = buildDummySubmodelWithSmElement(ID, submodelElements); + SubmodelService submodelService = getSubmodelService(submodel); + + Entity subEntity = new DefaultEntity.Builder().idShort("SubEntity").build(); + submodelService.createSubmodelElement("MainEntity", subEntity); + + + Entity sub_subEntity = new DefaultEntity.Builder().idShort("Sub_SubEntity").build(); + submodelService.createSubmodelElement("MainEntity.SubEntity", sub_subEntity); + + SubmodelElement sme = submodelService.getSubmodelElement("MainEntity.SubEntity.Sub_SubEntity"); + assertEquals(sub_subEntity.getIdShort(), sme.getIdShort()); + } + protected Submodel buildDummySubmodelWithSmElement(String id, List submodelElements) { return new DefaultSubmodel.Builder().id(id).submodelElements(submodelElements).build(); } From 165385cd461fd77c6657c9c8ef3b3b36d9e5ba48 Mon Sep 17 00:00:00 2001 From: FriedJannik Date: Wed, 28 Jan 2026 08:12:55 +0100 Subject: [PATCH 2/5] Adds ARE for create Submodel --- ...bmodelElementNotADataElementException.java | 46 +++++++++++++++++ .../backend/MongoDbSubmodelOperations.java | 14 ++++-- .../backend/MongoFilterBuilder.java | 36 ++++++++++--- .../submodelservice/SubmodelServiceSuite.java | 50 +++++++++++-------- 4 files changed, 112 insertions(+), 34 deletions(-) create mode 100644 basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/SubmodelElementNotADataElementException.java diff --git a/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/SubmodelElementNotADataElementException.java b/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/SubmodelElementNotADataElementException.java new file mode 100644 index 000000000..e5fa3fb1c --- /dev/null +++ b/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/SubmodelElementNotADataElementException.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.core.exceptions; + +/** + * Indicates that the requested submodel element is not a File SubmodelElement + * + * @author danish + * + */ +@SuppressWarnings("serial") +public class SubmodelElementNotADataElementException extends RuntimeException { + public SubmodelElementNotADataElementException() { + } + + public SubmodelElementNotADataElementException(String elementId) { + super(getMsg(elementId)); + } + + private static String getMsg(String elementId) { + return "SubmodelElement with Id " + elementId + " is not a Data Element"; + } +} diff --git a/basyx.submodelservice/basyx.submodelservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/MongoDbSubmodelOperations.java b/basyx.submodelservice/basyx.submodelservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/MongoDbSubmodelOperations.java index 82fb683c6..6309c9bbf 100644 --- a/basyx.submodelservice/basyx.submodelservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/MongoDbSubmodelOperations.java +++ b/basyx.submodelservice/basyx.submodelservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/MongoDbSubmodelOperations.java @@ -30,12 +30,9 @@ import java.util.List; import org.bson.Document; -import org.eclipse.digitaltwin.aas4j.v3.model.Entity; -import org.eclipse.digitaltwin.aas4j.v3.model.Submodel; -import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; -import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementCollection; -import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementList; +import org.eclipse.digitaltwin.aas4j.v3.model.*; import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; +import org.eclipse.digitaltwin.basyx.core.exceptions.SubmodelElementNotADataElementException; import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; import org.eclipse.digitaltwin.basyx.submodelservice.backend.SubmodelOperations; @@ -186,6 +183,13 @@ public void createSubmodelElement(String submodelId, String idShortPath, Submode } else if (parentSme instanceof Entity entity) { List submodelElements = entity.getStatements(); submodelElements.add(submodelElement); + } else if (parentSme instanceof AnnotatedRelationshipElement are) { + try { + List annotations = are.getAnnotations(); + annotations.add((DataElement) submodelElement); + } catch (ClassCastException e) { + throw new SubmodelElementNotADataElementException(submodelElement.getIdShort()); + } } updateSubmodelElement(submodelId, idShortPath, parentSme); diff --git a/basyx.submodelservice/basyx.submodelservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/MongoFilterBuilder.java b/basyx.submodelservice/basyx.submodelservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/MongoFilterBuilder.java index 7acedfd53..33c7d7331 100644 --- a/basyx.submodelservice/basyx.submodelservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/MongoFilterBuilder.java +++ b/basyx.submodelservice/basyx.submodelservice-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/MongoFilterBuilder.java @@ -29,6 +29,7 @@ import java.util.Deque; import java.util.List; +import org.eclipse.digitaltwin.aas4j.v3.model.AnnotatedRelationshipElement; import org.eclipse.digitaltwin.aas4j.v3.model.Entity; import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; import org.eclipse.digitaltwin.basyx.submodelservice.backend.IdShortPathParser.GenericPath; @@ -52,6 +53,7 @@ public final class MongoFilterBuilder{ static final String KEY_VALUE = "value"; static final String KEY_STATEMENTS = "statements"; + static final String KEY_ANNOTATIONS = "annotations"; static final String KEY_SUBMODEL_ELEMENTS = "submodelElements"; static final String KEY_ID_SHORT = "idShort"; @@ -88,11 +90,8 @@ public static MongoFilterResult parse(@NonNull String idShortPath, @NonNull List while (!paths.isEmpty()) { GenericPath segment = paths.pop(); - // Determine whether to use 'statements' (for Entity) or 'value' (for others) - String childKey = KEY_VALUE; - if (parentIndex < parentElements.size() && parentElements.get(parentIndex) instanceof Entity) { - childKey = KEY_STATEMENTS; - } + // Determine the correct child key based on parent element type + String childKey = getChildKey(parentElements, parentIndex); updateKey.append(".").append(childKey); parentIndex++; @@ -125,21 +124,44 @@ public static List buildAggregationOperations(@NonNull Str currentPath = paths.pop(); ops.add(new UnwindOperation(Fields.field("$"+KEY_VALUE), true)); ops.add(new UnwindOperation(Fields.field("$"+KEY_STATEMENTS), true)); + ops.add(new UnwindOperation(Fields.field("$"+KEY_ANNOTATIONS), true)); if (currentPath instanceof IdShortPath idPath) { Criteria inValue = Criteria.where(joinKeys(KEY_VALUE, KEY_ID_SHORT)).is(idPath.idShort()); Criteria inStatements = Criteria.where(joinKeys(KEY_STATEMENTS, KEY_ID_SHORT)).is(idPath.idShort()); - ops.add(Aggregation.match(new Criteria().orOperator(inValue, inStatements))); + Criteria inAnnotations = Criteria.where(joinKeys(KEY_ANNOTATIONS, KEY_ID_SHORT)).is(idPath.idShort()); + ops.add(Aggregation.match(new Criteria().orOperator(inValue, inStatements, inAnnotations))); } else if (currentPath instanceof IndexPath ixPath) { ops.add(Aggregation.skip(ixPath.index())); ops.add(Aggregation.limit(1)); } - ops.add(Aggregation.replaceRoot(IfNull.ifNull("$"+KEY_VALUE).then("$"+KEY_STATEMENTS))); + ops.add(Aggregation.replaceRoot( + IfNull.ifNull("$"+KEY_VALUE) + .thenValueOf(IfNull.ifNull("$"+KEY_STATEMENTS).then("$"+KEY_ANNOTATIONS)))); } return ops; } + /** + * Determines the correct child key based on the parent element type. + * - Entity uses 'statements' + * - AnnotatedRelationshipElement uses 'annotations' + * - Others (SubmodelElementList, SubmodelElementCollection) use 'value' + */ + private static String getChildKey(List parentElements, int parentIndex) { + if (parentIndex >= parentElements.size()) { + return KEY_VALUE; + } + SubmodelElement parent = parentElements.get(parentIndex); + if (parent instanceof Entity) { + return KEY_STATEMENTS; + } else if (parent instanceof AnnotatedRelationshipElement) { + return KEY_ANNOTATIONS; + } + return KEY_VALUE; + } + static CriteriaDefinition buildCriteria(String key, String value) { return Criteria.where(key).is(value); } diff --git a/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceSuite.java b/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceSuite.java index c513136d0..1ac6b41c1 100644 --- a/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceSuite.java +++ b/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceSuite.java @@ -41,28 +41,9 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.NotImplementedException; -import org.eclipse.digitaltwin.aas4j.v3.model.DataTypeDefXsd; -import org.eclipse.digitaltwin.aas4j.v3.model.Entity; -import org.eclipse.digitaltwin.aas4j.v3.model.File; -import org.eclipse.digitaltwin.aas4j.v3.model.LangStringTextType; -import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable; -import org.eclipse.digitaltwin.aas4j.v3.model.Property; -import org.eclipse.digitaltwin.aas4j.v3.model.Range; -import org.eclipse.digitaltwin.aas4j.v3.model.Submodel; -import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; -import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementCollection; -import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementList; -import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultEntity; -import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultFile; -import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultLangStringTextType; -import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty; -import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodel; -import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementCollection; -import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementList; -import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; -import org.eclipse.digitaltwin.basyx.core.exceptions.ElementNotAFileException; -import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException; -import org.eclipse.digitaltwin.basyx.core.exceptions.NotInvokableException; +import org.eclipse.digitaltwin.aas4j.v3.model.*; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.*; +import org.eclipse.digitaltwin.basyx.core.exceptions.*; import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; import org.eclipse.digitaltwin.basyx.submodelservice.value.FileBlobValue; @@ -689,6 +670,31 @@ public void addEntityToEntity() { assertEquals(sub_subEntity.getIdShort(), sme.getIdShort()); } + @Test + public void addDataElementToARE(){ + List submodelElements = new ArrayList<>(); + AnnotatedRelationshipElement topAre = new DefaultAnnotatedRelationshipElement.Builder().idShort("MainAre").build(); + Property property = createDummyProperty("Test"); + submodelElements.add(topAre); + Submodel submodel = buildDummySubmodelWithSmElement(ID, submodelElements); + SubmodelService submodelService = getSubmodelService(submodel); + + submodelService.createSubmodelElement("MainAre", property); + SubmodelElement sme = submodelService.getSubmodelElement("MainAre.Test"); + assertEquals(sme.getIdShort(), sme.getIdShort()); + } + + @Test(expected = SubmodelElementNotADataElementException.class) + public void addNonDataElementToAre(){ + List submodelElements = new ArrayList<>(); + AnnotatedRelationshipElement topAre = new DefaultAnnotatedRelationshipElement.Builder().idShort("MainAre").build(); + AnnotatedRelationshipElement subAre = new DefaultAnnotatedRelationshipElement.Builder().idShort("SubAre").build(); + submodelElements.add(topAre); + Submodel submodel = buildDummySubmodelWithSmElement(ID, submodelElements); + SubmodelService submodelService = getSubmodelService(submodel); + submodelService.createSubmodelElement("MainAre", subAre); + } + protected Submodel buildDummySubmodelWithSmElement(String id, List submodelElements) { return new DefaultSubmodel.Builder().id(id).submodelElements(submodelElements).build(); } From 454ae29b9de6566db9e07ff6bf5787f7c673db38 Mon Sep 17 00:00:00 2001 From: FriedJannik Date: Wed, 28 Jan 2026 08:13:40 +0100 Subject: [PATCH 3/5] Edits --- .../exceptions/SubmodelElementNotADataElementException.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/SubmodelElementNotADataElementException.java b/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/SubmodelElementNotADataElementException.java index e5fa3fb1c..9184bd488 100644 --- a/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/SubmodelElementNotADataElementException.java +++ b/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/SubmodelElementNotADataElementException.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2026 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 @@ -26,9 +26,9 @@ package org.eclipse.digitaltwin.basyx.core.exceptions; /** - * Indicates that the requested submodel element is not a File SubmodelElement + * Indicates that the requested submodel element is not a Data SubmodelElement * - * @author danish + * @author fried * */ @SuppressWarnings("serial") From 986288e1c464503027dcfa39e32da0dbf157b0d3 Mon Sep 17 00:00:00 2001 From: FriedJannik Date: Wed, 28 Jan 2026 08:28:54 +0100 Subject: [PATCH 4/5] Fixes ARE --- .../basyx/submodelservice/InMemorySubmodelBackend.java | 8 ++++++++ .../HierarchicalSubmodelElementIdShortPathToken.java | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelBackend.java b/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelBackend.java index eb3d171ac..cde96ebf2 100644 --- a/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelBackend.java +++ b/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelBackend.java @@ -29,6 +29,7 @@ import org.eclipse.digitaltwin.basyx.common.backend.inmemory.core.InMemoryCrudRepository; import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException; import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; +import org.eclipse.digitaltwin.basyx.core.exceptions.SubmodelElementNotADataElementException; import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationSupport; @@ -126,6 +127,13 @@ public synchronized void createSubmodelElement(String submodelId, String idShort } else if (parentSme instanceof Entity entity) { List submodelElements = entity.getStatements(); submodelElements.add(submodelElement); + } else if (parentSme instanceof AnnotatedRelationshipElement are) { + try { + List annotations = are.getAnnotations(); + annotations.add((DataElement) submodelElement); + } catch (ClassCastException e) { + throw new SubmodelElementNotADataElementException(submodelElement.getIdShort()); + } } } diff --git a/basyx.submodelservice/basyx.submodelservice-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/pathparsing/HierarchicalSubmodelElementIdShortPathToken.java b/basyx.submodelservice/basyx.submodelservice-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/pathparsing/HierarchicalSubmodelElementIdShortPathToken.java index 46cc6eadb..8d73837ed 100644 --- a/basyx.submodelservice/basyx.submodelservice-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/pathparsing/HierarchicalSubmodelElementIdShortPathToken.java +++ b/basyx.submodelservice/basyx.submodelservice-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/pathparsing/HierarchicalSubmodelElementIdShortPathToken.java @@ -25,7 +25,9 @@ package org.eclipse.digitaltwin.basyx.submodelservice.pathparsing; import java.util.Collection; +import java.util.stream.Collectors; +import org.eclipse.digitaltwin.aas4j.v3.model.AnnotatedRelationshipElement; import org.eclipse.digitaltwin.aas4j.v3.model.Entity; import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementCollection; @@ -59,6 +61,12 @@ public SubmodelElement getSubmodelElement(SubmodelElement rootElement) { Entity entity = (Entity) rootElement; return filterSubmodelElement(entity.getStatements()); + } else if (rootElement instanceof AnnotatedRelationshipElement) { + AnnotatedRelationshipElement are = (AnnotatedRelationshipElement) rootElement; + + return filterSubmodelElement(are.getAnnotations().stream() + .map(de -> (SubmodelElement) de) + .collect(Collectors.toList())); } throw new ElementDoesNotExistException(token); From 74235672ada3a276625c48a523ace19de9360d86 Mon Sep 17 00:00:00 2001 From: FriedJannik Date: Wed, 28 Jan 2026 08:46:45 +0100 Subject: [PATCH 5/5] Fixes ARE --- .../digitaltwin/basyx/http/BaSyxExceptionHandler.java | 7 ++++++- .../client/ConnectedSubmodelService.java | 10 +++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/basyx.common/basyx.http/src/main/java/org/eclipse/digitaltwin/basyx/http/BaSyxExceptionHandler.java b/basyx.common/basyx.http/src/main/java/org/eclipse/digitaltwin/basyx/http/BaSyxExceptionHandler.java index 2dd2513bb..97d6edd9e 100644 --- a/basyx.common/basyx.http/src/main/java/org/eclipse/digitaltwin/basyx/http/BaSyxExceptionHandler.java +++ b/basyx.common/basyx.http/src/main/java/org/eclipse/digitaltwin/basyx/http/BaSyxExceptionHandler.java @@ -103,11 +103,16 @@ public ResponseEntity handleFeatureNotSupportedException(FeatureNotSuppo public ResponseEntity handleNotInvokableException(NotInvokableException exception) { return buildResponse(exception.getMessage(), HttpStatus.METHOD_NOT_ALLOWED, exception); } - + @ExceptionHandler(ElementNotAFileException.class) public ResponseEntity handleElementNotAFileException(ElementNotAFileException exception) { return buildResponse(exception.getMessage(), HttpStatus.PRECONDITION_FAILED, exception); } + + @ExceptionHandler(SubmodelElementNotADataElementException.class) + public ResponseEntity handleSubmodelElementNotADataElementException(SubmodelElementNotADataElementException exception) { + return buildResponse(exception.getMessage(), HttpStatus.BAD_REQUEST, exception); + } @ExceptionHandler(InsufficientPermissionException.class) public ResponseEntity handleInsufficientPermissionException(InsufficientPermissionException exception) { diff --git a/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/ConnectedSubmodelService.java b/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/ConnectedSubmodelService.java index 7a84d32d6..a31ad8c0c 100644 --- a/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/ConnectedSubmodelService.java +++ b/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/ConnectedSubmodelService.java @@ -38,11 +38,7 @@ import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultOperationRequest; import org.eclipse.digitaltwin.basyx.client.internal.ApiException; -import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; -import org.eclipse.digitaltwin.basyx.core.exceptions.ElementNotAFileException; -import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException; -import org.eclipse.digitaltwin.basyx.core.exceptions.NotInvokableException; -import org.eclipse.digitaltwin.basyx.core.exceptions.OperationDelegationException; +import org.eclipse.digitaltwin.basyx.core.exceptions.*; import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; import org.eclipse.digitaltwin.basyx.http.Base64UrlEncoder; @@ -226,6 +222,10 @@ private RuntimeException mapExceptionSubmodelElementAccess(String idShortPath, A if (e.getCode() == HttpStatus.NOT_FOUND.value()) { return new ElementDoesNotExistException(idShortPath); } + System.out.println(e.getMessage()); + if (e.getCode() == HttpStatus.BAD_REQUEST.value() && e.getMessage().contains("is not a Data Element")) { + return new SubmodelElementNotADataElementException(idShortPath); + } return e; }