From 3ccb75b06cef51ba930d8159ad763fc1bc0769c9 Mon Sep 17 00:00:00 2001 From: Balduin Landolt <33053745+BalduinLandolt@users.noreply.github.com> Date: Tue, 7 Nov 2023 13:09:48 +0100 Subject: [PATCH] fix: retain value permissions if a link gets stashed because of circular dependencies (DEV-1489) (#623) --- src/dsp_tools/models/permission.py | 2 +- .../utils/xmlupload/stash/stash_models.py | 3 +-- .../utils/xmlupload/stash_circular_references.py | 8 +++++++- .../xmlupload/upload_stashed_resptr_props.py | 15 +++++++++------ src/dsp_tools/utils/xmlupload/xmlupload.py | 2 +- .../test_stash_circular_references.py | 3 ++- 6 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/dsp_tools/models/permission.py b/src/dsp_tools/models/permission.py index 122bb695a..98e2a3814 100644 --- a/src/dsp_tools/models/permission.py +++ b/src/dsp_tools/models/permission.py @@ -49,7 +49,7 @@ def __missing__(self, key: PermissionValue) -> None: def __contains__(self, key: PermissionValue) -> bool: return key in self._permissions - def __str__(self): + def __str__(self) -> str: tmpstr = "" for permission, groups in self._permissions.items(): if tmpstr: diff --git a/src/dsp_tools/utils/xmlupload/stash/stash_models.py b/src/dsp_tools/utils/xmlupload/stash/stash_models.py index 3e18f9958..5165295b8 100644 --- a/src/dsp_tools/utils/xmlupload/stash/stash_models.py +++ b/src/dsp_tools/utils/xmlupload/stash/stash_models.py @@ -15,8 +15,6 @@ class StandoffStashItem: uuid: str prop_name: str value: KnoraStandoffXml - # TODO: replace KnoraStandoffXml with str #pylint: disable=fixme - # TODO: Permissions missing still #pylint: disable=fixme @dataclass(frozen=True) @@ -50,6 +48,7 @@ class LinkValueStashItem: res_type: str prop_name: str target_id: str + permission: str | None = None @dataclass(frozen=True) diff --git a/src/dsp_tools/utils/xmlupload/stash_circular_references.py b/src/dsp_tools/utils/xmlupload/stash_circular_references.py index a48854338..df04a35d4 100644 --- a/src/dsp_tools/utils/xmlupload/stash_circular_references.py +++ b/src/dsp_tools/utils/xmlupload/stash_circular_references.py @@ -10,6 +10,7 @@ generate_upload_order, make_graph, ) +from dsp_tools.models.permission import Permissions from dsp_tools.models.value import KnoraStandoffXml from dsp_tools.models.xmlproperty import XMLProperty from dsp_tools.models.xmlresource import XMLResource @@ -56,17 +57,20 @@ def _stash_resptr( restype: str, link_prop: XMLProperty, stash_lookup: dict[str, list[str]], + permission_lookup: dict[str, Permissions], ) -> list[LinkValueStashItem]: stashed_items = [] for value in link_prop.values.copy(): if value.link_uuid not in stash_lookup[res_id]: continue + permission = str(permission_lookup[value.permissions]) if value.permissions else None # value.value is the ID of the target resource. stash it, then delete it link_stash_item = LinkValueStashItem( res_id=res_id, res_type=restype, prop_name=link_prop.name, target_id=str(value.value), + permission=permission, ) link_prop.values.remove(value) stashed_items.append(link_stash_item) @@ -76,6 +80,7 @@ def _stash_resptr( def stash_circular_references( resources: list[XMLResource], stash_lookup: dict[str, list[str]], + permission_lookup: dict[str, Permissions], ) -> Stash | None: """ Stashes problematic resource-references from a list of resources. @@ -84,6 +89,7 @@ def stash_circular_references( Args: resources: all resources of the XML file stash_lookup: A dictionary which maps the resources that have stashes to the UUIDs of the stashed links + permission_lookup: A dictionary which maps the permissions of the stashed links to their string representation Returns: stash: an object that contains the stashed references @@ -100,7 +106,7 @@ def stash_circular_references( standoff_stash_item = _stash_standoff(res.id, res.restype, link_prop, stash_lookup) stashed_standoff_values.extend(standoff_stash_item) elif link_prop.valtype == "resptr": - link_stash_item = _stash_resptr(res.id, res.restype, link_prop, stash_lookup) + link_stash_item = _stash_resptr(res.id, res.restype, link_prop, stash_lookup, permission_lookup) stashed_link_values.extend(link_stash_item) if len(link_prop.values) == 0: diff --git a/src/dsp_tools/utils/xmlupload/upload_stashed_resptr_props.py b/src/dsp_tools/utils/xmlupload/upload_stashed_resptr_props.py index 9c4e2e6f8..b0c4604ca 100644 --- a/src/dsp_tools/utils/xmlupload/upload_stashed_resptr_props.py +++ b/src/dsp_tools/utils/xmlupload/upload_stashed_resptr_props.py @@ -82,7 +82,7 @@ def _upload_stash_item( except BaseError as err: _log_unable_to_upload_link_value(err.orig_err_msg_from_api or err.message, stash.res_id, stash.prop_name) return False - logger.debug(f' Successfully uploaded xml text of "{stash.prop_name}"') + logger.debug(f' Successfully uploaded resptr links of "{stash.prop_name}"') return True @@ -99,14 +99,17 @@ def _create_resptr_prop_json_object_to_update( context: dict[str, str], ) -> str: """This function creates a JSON object that can be sent as an update request to the DSP-API.""" + linkVal = { + "@type": "knora-api:LinkValue", + "knora-api:linkValueHasTargetIri": {"@id": target_iri}, + } + if stash.permission: + linkVal["knora-api:hasPermissions"] = stash.permission jsonobj = { "@id": res_iri, "@type": stash.res_type, - f"{stash.prop_name}Value": { - "@type": "knora-api:LinkValue", - "knora-api:linkValueHasTargetIri": {"@id": target_iri}, - }, + f"{stash.prop_name}Value": linkVal, "@context": context, } - jsondata = json.dumps(jsonobj, indent=4, separators=(",", ": ")) + jsondata = json.dumps(jsonobj) return jsondata diff --git a/src/dsp_tools/utils/xmlupload/xmlupload.py b/src/dsp_tools/utils/xmlupload/xmlupload.py index a7271512e..6f65b6b99 100644 --- a/src/dsp_tools/utils/xmlupload/xmlupload.py +++ b/src/dsp_tools/utils/xmlupload/xmlupload.py @@ -142,7 +142,7 @@ def _prepare_upload( logger.info("Stashing circular references...") if verbose: print("Stashing circular references...") - stash = stash_circular_references(resources, stash_lookup) + stash = stash_circular_references(resources, stash_lookup, permissions_lookup) return resources, permissions_lookup, stash diff --git a/test/benchmarking/test_stash_circular_references.py b/test/benchmarking/test_stash_circular_references.py index 3e0dec5fa..7571c0e5b 100644 --- a/test/benchmarking/test_stash_circular_references.py +++ b/test/benchmarking/test_stash_circular_references.py @@ -3,6 +3,7 @@ import pytest from termcolor import cprint +from dsp_tools.models.permission import Permissions from dsp_tools.utils.xml_utils import parse_and_clean_xml_file from dsp_tools.utils.xmlupload.stash_circular_references import identify_circular_references, stash_circular_references from dsp_tools.utils.xmlupload.xmlupload import _extract_resources_from_xml @@ -12,7 +13,7 @@ def test_get_length_ok_resources() -> None: test_root = parse_and_clean_xml_file("testdata/xml-data/circular-references/test_circular_references_1.xml") stash_lookup, _ = identify_circular_references(test_root) resources = _extract_resources_from_xml(test_root, "simcir") - stash = stash_circular_references(resources, stash_lookup) + stash = stash_circular_references(resources, stash_lookup, {"prop-default": Permissions()}) len_standoff = len(stash.standoff_stash.res_2_stash_items) # type: ignore[union-attr] len_resptr = len(stash.link_value_stash.res_2_stash_items) # type: ignore[union-attr] stashed_links = len_standoff + len_resptr