Skip to content

Commit ab63593

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 ab63593

1 file changed

Lines changed: 68 additions & 3 deletions

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: 68 additions & 3 deletions
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,73 @@ 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 {
106-
deleteAssociatedFileIfAny(idShortPath);
107-
111+
if (isFileElement(submodelElement)) {
112+
handleFileAttachmentUpdate(submodelId, idShortPath, submodelElement);
113+
}
108114
backend.updateSubmodelElement(submodelId, idShortPath, submodelElement);
109115
}
110116

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

0 commit comments

Comments
 (0)