Skip to content

Commit 0737029

Browse files
authored
Opt in to dropping revision on metadata update. (#972)
* Opt in to dropping revision on metadata update. * Update CHANGELOG * Use unique key (Windows Zarr test failure)
1 parent c807c87 commit 0737029

File tree

5 files changed

+85
-26
lines changed

5 files changed

+85
-26
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22
Write the date in place of the "Unreleased" in the case a new version is released. -->
33
# Changelog
44

5+
## Unreleased
6+
7+
### Added
8+
9+
- New query parameter `drop_revision` on endpoints `PUT /metadata/{path}`
10+
and `PATCH /metadata/{path}`. If set to true, the version replaced by
11+
the update is _not_ saved as a revision. This is exposed in the Python
12+
client via a new keyword-only argument `drop_revision` in
13+
`update_metadata`, `patch_metadata`, and `replace_metadata`.
14+
515
## 0.1.0-b25 (2025-05-06)
616

717
### Added

tiled/_tests/test_writing.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,23 @@ def test_metadata_revisions(tree):
406406
ac.metadata_revisions.delete_revision(1)
407407

408408

409+
def test_drop_revision(tree):
410+
key = "test_drop_revision"
411+
with Context.from_app(build_app(tree)) as context:
412+
client = from_context(context)
413+
# Set metadata color=blue.
414+
ac = client.write_array([1, 2, 3], metadata={"color": "blue"}, key=key)
415+
assert ac.metadata["color"] == "blue"
416+
assert client[key].metadata["color"] == "blue"
417+
assert len(ac.metadata_revisions[:]) == 0
418+
# Update metadata to color=red, but drop revision.
419+
ac.update_metadata(metadata={"color": "red"}, drop_revision=True)
420+
# Metadata is updated; no revision is saved.
421+
assert ac.metadata["color"] == "red"
422+
assert client[key].metadata["color"] == "red"
423+
assert len(ac.metadata_revisions) == 0
424+
425+
409426
def test_merge_patching(tree):
410427
"Test merge patching of metadata and specs"
411428
with Context.from_app(build_app(tree)) as context:

tiled/catalog/adapter.py

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -955,7 +955,7 @@ async def delete_revision(self, number):
955955
), f"Deletion would affect {result.rowcount} rows; rolling back"
956956
await db.commit()
957957

958-
async def replace_metadata(self, metadata=None, specs=None):
958+
async def replace_metadata(self, metadata=None, specs=None, *, drop_revision=False):
959959
values = {}
960960
if metadata is not None:
961961
# Trailing underscore in 'metadata_' avoids collision with
@@ -964,28 +964,31 @@ async def replace_metadata(self, metadata=None, specs=None):
964964
if specs is not None:
965965
values["specs"] = [s.model_dump() for s in specs]
966966
async with self.context.session() as db:
967-
current = (
968-
await db.execute(select(orm.Node).where(orm.Node.id == self.node.id))
969-
).scalar_one()
970-
next_revision_number = 1 + (
971-
(
967+
if not drop_revision:
968+
current = (
972969
await db.execute(
973-
select(func.max(orm.Revision.revision_number)).where(
974-
orm.Revision.node_id == self.node.id
975-
)
970+
select(orm.Node).where(orm.Node.id == self.node.id)
976971
)
977972
).scalar_one()
978-
or 0
979-
)
980-
revision = orm.Revision(
981-
# Trailing underscore in 'metadata_' avoids collision with
982-
# SQLAlchemy reserved word 'metadata'.
983-
metadata_=current.metadata_,
984-
specs=current.specs,
985-
node_id=current.id,
986-
revision_number=next_revision_number,
987-
)
988-
db.add(revision)
973+
next_revision_number = 1 + (
974+
(
975+
await db.execute(
976+
select(func.max(orm.Revision.revision_number)).where(
977+
orm.Revision.node_id == self.node.id
978+
)
979+
)
980+
).scalar_one()
981+
or 0
982+
)
983+
revision = orm.Revision(
984+
# Trailing underscore in 'metadata_' avoids collision with
985+
# SQLAlchemy reserved word 'metadata'.
986+
metadata_=current.metadata_,
987+
specs=current.specs,
988+
node_id=current.id,
989+
revision_number=next_revision_number,
990+
)
991+
db.add(revision)
989992
await db.execute(
990993
update(orm.Node).where(orm.Node.id == self.node.id).values(**values)
991994
)

tiled/client/base.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ def formats(self):
446446
)
447447
return sorted(formats)
448448

449-
def update_metadata(self, metadata=None, specs=None):
449+
def update_metadata(self, metadata=None, specs=None, *, drop_revision=False):
450450
"""
451451
EXPERIMENTAL: Update metadata via a `dict.update`- like interface.
452452
@@ -461,6 +461,9 @@ def update_metadata(self, metadata=None, specs=None):
461461
specs : List[str], optional
462462
List of names that are used to label that the data and/or metadata
463463
conform to some named standard specification.
464+
drop_revision : bool, optional
465+
Replace current version without saving current version as a revision.
466+
Use with caution.
464467
465468
See Also
466469
--------
@@ -506,7 +509,11 @@ def update_metadata(self, metadata=None, specs=None):
506509
metadata_patch, specs_patch = self.build_metadata_patches(
507510
metadata=metadata, specs=specs
508511
)
509-
self.patch_metadata(metadata_patch=metadata_patch, specs_patch=specs_patch)
512+
self.patch_metadata(
513+
metadata_patch=metadata_patch,
514+
specs_patch=specs_patch,
515+
drop_revision=drop_revision,
516+
)
510517

511518
def build_metadata_patches(self, metadata=None, specs=None):
512519
"""
@@ -619,6 +626,7 @@ def patch_metadata(
619626
metadata_patch=None,
620627
specs_patch=None,
621628
content_type=patch_mimetypes.JSON_PATCH,
629+
drop_revision=False,
622630
):
623631
"""
624632
EXPERIMENTAL: Patch metadata using a JSON Patch (RFC6902).
@@ -639,6 +647,9 @@ def patch_metadata(
639647
(See https://datatracker.ietf.org/doc/html/rfc6902)
640648
* "application/merge-patch+json"
641649
(See https://datatracker.ietf.org/doc/html/rfc7386)
650+
drop_revision : bool, optional
651+
Replace current version without saving current version as a revision.
652+
Use with caution.
642653
643654
See Also
644655
--------
@@ -689,13 +700,17 @@ def patcher(doc, patch, patch_type):
689700
"metadata": metadata_patch,
690701
"specs": normalized_specs_patch,
691702
}
703+
params = {}
704+
if drop_revision:
705+
params["drop_revision"] = True
692706

693707
for attempt in retry_context():
694708
with attempt:
695709
content = handle_error(
696710
self.context.http_client.patch(
697711
self.item["links"]["self"],
698712
content=safe_json_dump(data),
713+
params=params,
699714
)
700715
).json()
701716

@@ -716,7 +731,7 @@ def patcher(doc, patch, patch_type):
716731
patched_specs = patcher(current_specs, normalized_specs_patch, content_type)
717732
self._item["attributes"]["specs"] = patched_specs
718733

719-
def replace_metadata(self, metadata=None, specs=None):
734+
def replace_metadata(self, metadata=None, specs=None, drop_revision=False):
720735
"""
721736
EXPERIMENTAL: Replace metadata entirely (see update_metadata).
722737
@@ -730,6 +745,9 @@ def replace_metadata(self, metadata=None, specs=None):
730745
specs : List[str], optional
731746
List of names that are used to label that the data and/or metadata
732747
conform to some named standard specification.
748+
drop_revision : bool, optional
749+
Replace current version without saving current version as a revision.
750+
Use with caution.
733751
734752
See Also
735753
--------
@@ -751,12 +769,17 @@ def replace_metadata(self, metadata=None, specs=None):
751769
"metadata": metadata,
752770
"specs": normalized_specs,
753771
}
772+
params = {}
773+
if drop_revision:
774+
params["drop_revision"] = True
754775

755776
for attempt in retry_context():
756777
with attempt:
757778
content = handle_error(
758779
self.context.http_client.put(
759-
self.item["links"]["self"], content=safe_json_dump(data)
780+
self.item["links"]["self"],
781+
content=safe_json_dump(data),
782+
params=params,
760783
)
761784
).json()
762785

tiled/server/router.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,6 +1429,7 @@ async def patch_metadata(
14291429
body: schemas.PatchMetadataRequest,
14301430
settings: Settings = Depends(get_settings),
14311431
entry: MapAdapter = Security(get_entry(), scopes=["write:metadata"]),
1432+
drop_revision: bool = False,
14321433
):
14331434
if not hasattr(entry, "replace_metadata"):
14341435
raise HTTPException(
@@ -1476,7 +1477,9 @@ async def patch_metadata(
14761477
settings=settings,
14771478
)
14781479

1479-
await entry.replace_metadata(metadata=metadata, specs=specs)
1480+
await entry.replace_metadata(
1481+
metadata=metadata, specs=specs, drop_revision=drop_revision
1482+
)
14801483

14811484
response_data = {"id": entry.key}
14821485
if metadata_modified:
@@ -1489,6 +1492,7 @@ async def put_metadata(
14891492
body: schemas.PutMetadataRequest,
14901493
settings: Settings = Depends(get_settings),
14911494
entry: MapAdapter = Security(get_entry(), scopes=["write:metadata"]),
1495+
drop_revision: bool = False,
14921496
):
14931497
if not hasattr(entry, "replace_metadata"):
14941498
raise HTTPException(
@@ -1511,7 +1515,9 @@ async def put_metadata(
15111515
settings=settings,
15121516
)
15131517

1514-
await entry.replace_metadata(metadata=metadata, specs=specs)
1518+
await entry.replace_metadata(
1519+
metadata=metadata, specs=specs, drop_revision=drop_revision
1520+
)
15151521

15161522
response_data = {"id": entry.key}
15171523
if metadata_modified:

0 commit comments

Comments
 (0)