Skip to content

Persistence fix and PATCH endpoints for submodel elements#78

Open
tamasf1 wants to merge 12 commits into
aas-demonstratorfrom
fix/persistence-and-patch
Open

Persistence fix and PATCH endpoints for submodel elements#78
tamasf1 wants to merge 12 commits into
aas-demonstratorfrom
fix/persistence-and-patch

Conversation

@tamasf1
Copy link
Copy Markdown

@tamasf1 tamasf1 commented May 27, 2026

Mutations applied via PUT and PATCH endpoints were silently discarded.

The LocalFileIdentifiableStore reads objects from disk on every GET request and calls update_from() to refresh the in-memory cache. Because update_from() only mutates the in-memory object without writing back to disk, any changes made via PUT were silently overwritten on the next GET. As a result, PUT requests appeared to succeed (HTTP 204) but the repository state was never actually updated.

A new commit() method on LocalFileIdentifiableStore writes an Identifiable back to its storage file after mutation. The PUT and PATCH handlers in the repository interface now call commit() on the parent submodel after update_from() or value-only patching.

Additionally, six PATCH endpoints that previously returned 501 are now implemented:

  • PATCH /submodels/{smId}
  • PATCH /submodels/{smId}/$metadata
  • PATCH /submodels/{smId}/$value
  • PATCH /submodels/{smId}/submodel-elements/{path}
  • PATCH /submodels/{smId}/submodel-elements/{path}/$metadata
  • PATCH /submodels/{smId}/submodel-elements/{path}/$value

The $value PATCH handler covers Property, MultiLanguageProperty, Range, Blob, File, SubmodelElementCollection and SubmodelElementList. Complex element types (ReferenceElement, RelationshipElement) are not yet supported and return 400.

zrgt and others added 12 commits May 13, 2026 15:05
eclipse-basyx#541)

- Fix SUBMODEL_READ/SUBMODEL_VALUE names (were swapped: SSP-002=Read, SSP-003=Value)
- Rename AAS_REPOSITORY_BULK -> AAS_REPOSITORY_QUERY (SSP-003 is Query, no Bulk exists)
- Rename SUBMODEL_REPOSITORY_BULK -> SUBMODEL_REPOSITORY_TEMPLATE (SSP-003 is Template)
- Rename CONCEPT_DESCRIPTION_REPOSITORY_READ -> CONCEPT_DESCRIPTION_REPOSITORY_QUERY (SSP-002)
- Remove CONCEPT_DESCRIPTION_REPOSITORY_BULK (SSP-003 does not exist in spec)
- Add AAS_REGISTRY_QUERY (SSP-004), AAS_REGISTRY_MINIMAL_READ (SSP-005)
- Add SUBMODEL_REGISTRY_QUERY (SSP-004)
- Add SUBMODEL_REPOSITORY_TEMPLATE_READ (SSP-004), SUBMODEL_REPOSITORY_QUERY (SSP-005)
…#514)

pop() only removed item from first backend via popitem(), leaving stale
entries in remaining backends and causing false AASConstraintViolation
on subsequent add() for same semantic_id.

Fixes eclipse-basyx#496
Previously `load_directory()` called `read_server_aas_json_file_into()`, which internally only adds items where `isinstance(item, model.Identifiable)`. `AssetAdministrationShellDescriptor` and `SubmodelDescriptor` are not `Identifiable`, so all descriptors were silently skipped. The registry always started empty.

This parses descriptor JSON directly with `ServerAASFromJsonDecoder` and add items to `DictDescriptorStore`.

Fixes eclipse-basyx#544
…les (eclipse-basyx#507)

Previously, `LocalFileIdentifiableStore` counted any file in it's storage directory for its `__len__`. 

Now it only counts `.json` files. Additional unittests ensure that the file ending used in  `__len__` fits with the way files are written in the store.

Fixes eclipse-basyx#499
Fixes eclipse-basyx#503
…clipse-basyx#510)

Previously, `DataSpecificationIEC61360.value` was dropped silently, if `DataSpecificationIEC61360.value_format` was `None` in the XML deserialization. There's no constraint (anymore) that enforces this.

Therefore, we change the code to deserialize `value` independent of `value_format`. 

Fixes eclipse-basyx#501
…que_name (eclipse-basyx#513)

`DictSupplementaryFileContainer_store_refcount` is designed to track how many `_name_map` entries reference the same content hash, so `delete_file()` can free the underlying bytes only when the last reference is removed. The refcount is never incremented, so files are never freed.

Previously, because `_assign_unique_name()` never increments `_store_refcount`, the count stays at 0 after any `add_file()`. Every `delete_file()` decrements to -1 and the equality check `== 0` is never true, so `_store[hash]` and `_store_refcount[hash]` are never cleaned up. Every file ever added leaks indefinitely.

This fixes this bug by incrementing `_store_refcount[sha] += 1` inside `_assign_unique_name()` when a new `_name_map` entry is created (the first branch of the `while True` loop). Also decrement it (and skip the increment) inside the second branch when a duplicate name already maps to the same hash.

Fixes eclipse-basyx#495
…rned empty results (eclipse-basyx#512)

When 2 or more `globalAssetId` query parameters were sent, a
`len(global_asset_ids) <= 1` guard in the filter lambda in
`repository.py` evaluated `False` for every shell, causing an
empty HTTP 200 response with no error.

The guard was likely intended to reject invalid input with a 400
error, not silently discard all results.

The guard is replaced with proper input validation that raises
`BadRequest` when multiple global asset IDs are provided.

Fixes eclipse-basyx#500
The compliance tool still had code to check a json or xml file
against the defined schema from admin-shell-io/aas-specs-metamodel.
The option to run this schema check was removed earlier from cli.py
with commit af73a4b.

As no other code uses the implemented schema check, this function
is now removed, including the schema files and the unittests.
Previously, `provider.py` imported `ServerAASFromJsonDecoder` directly
from `app.adapter`, which caused a circular import.

Reverted the import to use the `app.adapter` module reference instead,
accessing `adapter.ServerAASFromJsonDecoder` at the call site.

Co-authored-by: s-heppner <iat@s-heppner.com>
update_from() only mutated the in-memory object; the next GET re-read
the unchanged file from disk and overwrote the in-memory state, silently
discarding the update.

Add LocalFileIdentifiableStore.commit() to write an identifiable back to
its storage file, and call it from put_submodel and
put_submodel_submodel_elements_id_short_path after update_from().
Six routes were previously returning 501 Not Implemented:
- PATCH /submodels/{smId}
- PATCH /submodels/{smId}/$metadata
- PATCH /submodels/{smId}/$value
- PATCH /submodels/{smId}/submodel-elements/{path}
- PATCH /submodels/{smId}/submodel-elements/{path}/$metadata
- PATCH /submodels/{smId}/submodel-elements/{path}/$value

The plain and $metadata variants decode the full/stripped body and
call update_from. The $value variants use a new _apply_value_only
helper that applies a ValueOnly patch to the in-memory element tree.
All handlers call commit() to persist changes to disk.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants