Skip to content

Commit 4844a75

Browse files
committed
fix(file-sme): preserve file attachments when updating metadata
Previously, updating a File submodel element via PUT would unconditionally delete the associated file, even when the file path (value) did not change. This commit introduces logic to detect whether the file path has actually changed before deleting the file. Changes: - Added path comparison logic using java.nio.file.Path to handle OS-specific path formats. - Only delete attachments if the path has changed. - Introduced helper methods for clarity and testability: isFileElement, extractFilePath, getExistingFilePath, pathsDiffer. - Added robust exception handling and conservative fallback (delete if uncertain). - Added structured SLF4J logging for traceability. This resolves unintended data loss during metadata-only updates to File submodel elements.
1 parent 8bbddab commit 4844a75

1 file changed

Lines changed: 67 additions & 1 deletion

File tree

  • basyx.submodelservice/basyx.submodelservice-backend/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend

basyx.submodelservice/basyx.submodelservice-backend/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/backend/CrudSubmodelService.java

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,14 @@
3838
import org.eclipse.digitaltwin.basyx.operation.InvokableOperation;
3939
import org.eclipse.digitaltwin.basyx.submodelservice.SubmodelService;
4040
import org.eclipse.digitaltwin.basyx.submodelservice.value.SubmodelElementValue;
41+
import org.slf4j.Logger;
42+
import org.slf4j.LoggerFactory;
4143
import org.springframework.lang.NonNull;
4244

4345
import java.io.File;
4446
import java.io.InputStream;
47+
import java.nio.file.Path;
48+
import java.nio.file.Paths;
4549
import java.util.List;
4650

4751
/**
@@ -54,6 +58,7 @@ public class CrudSubmodelService implements SubmodelService {
5458
private final SubmodelBackend backend;
5559
private final String submodelId;
5660
private final SubmodelFileOperations submodelFileOperations;
61+
private Logger logger = LoggerFactory.getLogger(CrudSubmodelService.class);
5762

5863
public CrudSubmodelService(SubmodelBackend submodelRepositoryBackend, FileRepository fileRepository, @NonNull Submodel submodel) {
5964
this(submodelRepositoryBackend, fileRepository, submodel.getId());
@@ -101,13 +106,74 @@ public void createSubmodelElement(String idShortPath, SubmodelElement submodelEl
101106
backend.createSubmodelElement(submodelId, idShortPath, submodelElement);
102107
}
103108

104-
@Override
109+
@Override
105110
public void updateSubmodelElement(String idShortPath, SubmodelElement submodelElement) throws ElementDoesNotExistException {
111+
<<<<<<< HEAD
106112
deleteAssociatedFileIfAny(idShortPath);
113+
=======
114+
if (isFileElement(submodelElement)) {
115+
handleFileAttachmentUpdate(submodelId, idShortPath, submodelElement);
116+
}
117+
>>>>>>> aa91d0cc (fix(file-sme): preserve file attachments when updating metadata)
107118

108119
backend.updateSubmodelElement(submodelId, idShortPath, submodelElement);
109120
}
110121

122+
private void handleFileAttachmentUpdate(String submodelId, String idShortPath, SubmodelElement updatedElement) {
123+
try {
124+
if (hasFilePathChanged(submodelId, idShortPath, updatedElement)) {
125+
deleteAssociatedFileIfAny(idShortPath);
126+
}
127+
} catch (Exception e) {
128+
logger.warn("Could not verify file path change for '{}'. Assuming changed. Reason: {}", idShortPath, e.getMessage());
129+
deleteAssociatedFileIfAny(idShortPath);
130+
}
131+
}
132+
133+
private boolean hasFilePathChanged(String submodelId, String idShortPath, SubmodelElement updatedElement) {
134+
135+
String newPathString = extractFilePath((org.eclipse.digitaltwin.aas4j.v3.model.File) updatedElement);
136+
if (newPathString == null) {
137+
return true; // new path missing → handle as changed
138+
}
139+
140+
String oldPathString = getExistingFilePath(submodelId, idShortPath);
141+
if (oldPathString == null) {
142+
return true; // old path missing → handle as changed
143+
}
144+
145+
return pathsDiffer(oldPathString, newPathString);
146+
}
147+
148+
private boolean isFileElement(SubmodelElement element) {
149+
return element instanceof org.eclipse.digitaltwin.aas4j.v3.model.File;
150+
}
151+
152+
// Extracts the new file path (value) from the updated File element
153+
private String extractFilePath(org.eclipse.digitaltwin.aas4j.v3.model.File fileElement) {
154+
return fileElement.getValue();
155+
}
156+
157+
private String getExistingFilePath(String submodelId, String idShortPath) {
158+
try {
159+
return submodelFileOperations.getFile(submodelId, idShortPath).getPath();
160+
} catch (Exception e) {
161+
logger.warn("Failed to retrieve existing file path for '{}': {}", idShortPath, e.getMessage(), e);
162+
return null;
163+
}
164+
}
165+
166+
private boolean pathsDiffer(String path1, String path2) {
167+
try {
168+
Path normalized1 = Paths.get(path1).normalize().toAbsolutePath();
169+
Path normalized2 = Paths.get(path2).normalize().toAbsolutePath();
170+
return !normalized1.equals(normalized2);
171+
} catch (Exception e) {
172+
logger.error("Error comparing file paths: {}", e.getMessage(), e);
173+
return true; // Consider paths different in case of error
174+
}
175+
}
176+
111177
@Override
112178
public void deleteSubmodelElement(String idShortPath) throws ElementDoesNotExistException {
113179
deleteAssociatedFileIfAny(idShortPath);

0 commit comments

Comments
 (0)