From e83120eaa3344931996d6df7cc7ebeddc4e9cbae Mon Sep 17 00:00:00 2001 From: Stephen Rosen Date: Mon, 11 Dec 2023 16:05:15 -0600 Subject: [PATCH 1/2] Add `delete_protected` for mapped collections This is a new field in the MappedCollectionDocument definition. As a bit of new testing art to try to validate the change, the unit tests for the GCS helpers now include some signature-inspection tests + a test for optional bool settings. These are rather simplistic tests which might not offer a ton of value, but they're also very quick to execute. --- ...60420_sirosen_support_delete_protected.rst | 4 ++ .../services/gcs/data/collection.py | 6 ++ tests/unit/helpers/gcs/test_collections.py | 59 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 changelog.d/20231211_160420_sirosen_support_delete_protected.rst diff --git a/changelog.d/20231211_160420_sirosen_support_delete_protected.rst b/changelog.d/20231211_160420_sirosen_support_delete_protected.rst new file mode 100644 index 000000000..5a902526b --- /dev/null +++ b/changelog.d/20231211_160420_sirosen_support_delete_protected.rst @@ -0,0 +1,4 @@ +Added +~~~~~ + +- Add the ``delete_protected`` field to ``MappedCollectionDocument``. (:pr:`NUMBER`) diff --git a/src/globus_sdk/services/gcs/data/collection.py b/src/globus_sdk/services/gcs/data/collection.py index 74711c5d8..b890e7c93 100644 --- a/src/globus_sdk/services/gcs/data/collection.py +++ b/src/globus_sdk/services/gcs/data/collection.py @@ -236,6 +236,10 @@ class MappedCollectionDocument(CollectionDocument): guest collections :type sharing_users_deny: iterable of str, optional + :param delete_protected: Enable or disable deletion protection on this collection. + Defaults to ``True`` during creation. + :type delete_protected: bool, optional + :param allow_guest_collections: Enable or disable creation and use of Guest Collections on this Mapped Collection :type allow_guest_collections: bool, optional @@ -292,6 +296,7 @@ def __init__( sharing_users_deny: t.Iterable[str] | None = None, sharing_restrict_paths: dict[str, t.Any] | None = None, # bools + delete_protected: bool | None = None, allow_guest_collections: bool | None = None, disable_anonymous_writes: bool | None = None, # dicts @@ -337,6 +342,7 @@ def __init__( sharing_users_deny=sharing_users_deny, ) self._set_optbools( + delete_protected=delete_protected, allow_guest_collections=allow_guest_collections, disable_anonymous_writes=disable_anonymous_writes, ) diff --git a/tests/unit/helpers/gcs/test_collections.py b/tests/unit/helpers/gcs/test_collections.py index 47775526e..6b7772b3f 100644 --- a/tests/unit/helpers/gcs/test_collections.py +++ b/tests/unit/helpers/gcs/test_collections.py @@ -1,3 +1,4 @@ +import inspect import uuid import pytest @@ -16,6 +17,10 @@ STUB_UC_ID = uuid.uuid1() # user credential +MappedCollectionSignature = inspect.signature(MappedCollectionDocument) +GuestCollectionSignature = inspect.signature(GuestCollectionDocument) + + def test_collection_base_abstract(): with pytest.raises(TypeError): CollectionDocument() @@ -153,3 +158,57 @@ def test_collection_policies_field(policies_type): } else: raise NotImplementedError + + +# these test cases enumerate parameters for Guest Collections and Mapped Collections +# and ensure that they're defined on one class but not the other +@pytest.mark.parametrize( + "fieldname", + ( + "allow_guest_collections", + "delete_protected", + "disable_anonymous_writes", + "domain_name", + "guest_auth_policy_id", + "policies", + "sharing_restrict_paths", + "sharing_users_allow", + "sharing_users_deny", + "storage_gateway_id", + ), +) +def test_settings_which_are_only_supported_in_mapped_collections(fieldname): + assert fieldname in MappedCollectionSignature.parameters + assert fieldname not in GuestCollectionSignature.parameters + + +@pytest.mark.parametrize( + "fieldname", + ( + "mapped_collection_id", + "user_credential_id", + ), +) +def test_settings_which_are_only_supported_in_guest_collections(fieldname): + assert fieldname in GuestCollectionSignature.parameters + assert fieldname not in MappedCollectionSignature.parameters + + +@pytest.mark.parametrize( + "fieldname", + ( + "allow_guest_collections", + "delete_protected", + "disable_anonymous_writes", + ), +) +@pytest.mark.parametrize("value", (True, False, None)) +def test_mapped_collection_opt_bool(fieldname, value): + doc = MappedCollectionDocument( + storage_gateway_id=STUB_SG_ID, collection_base_path="/", **{fieldname: value} + ) + if value is not None: + assert fieldname in doc + assert doc[fieldname] == value + else: + assert fieldname not in doc From 99035ca8412172c036a63de26177f8aa2a4f7545 Mon Sep 17 00:00:00 2001 From: Stephen Rosen Date: Tue, 12 Dec 2023 10:43:20 -0600 Subject: [PATCH 2/2] Mapped Collection delete_protected implies v1.8.0 Fix the omission from DATATYPE_VERSION_IMPLICATIONS and add tests. --- src/globus_sdk/services/gcs/data/collection.py | 1 + tests/unit/helpers/gcs/test_collections.py | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/globus_sdk/services/gcs/data/collection.py b/src/globus_sdk/services/gcs/data/collection.py index b890e7c93..3e132c068 100644 --- a/src/globus_sdk/services/gcs/data/collection.py +++ b/src/globus_sdk/services/gcs/data/collection.py @@ -126,6 +126,7 @@ class CollectionDocument(utils.PayloadWrapper, abc.ABC): DATATYPE_BASE: str = "collection" DATATYPE_VERSION_IMPLICATIONS: dict[str, tuple[int, int, int]] = { + "delete_protected": (1, 8, 0), "guest_auth_policy_id": (1, 6, 0), "disable_anonymous_writes": (1, 5, 0), "force_verify": (1, 4, 0), diff --git a/tests/unit/helpers/gcs/test_collections.py b/tests/unit/helpers/gcs/test_collections.py index 6b7772b3f..405603024 100644 --- a/tests/unit/helpers/gcs/test_collections.py +++ b/tests/unit/helpers/gcs/test_collections.py @@ -71,6 +71,17 @@ def test_datatype_version_deduction(use_kwargs, doc_version): ({"disable_anonymous_writes": True}, "1.5.0"), ({"disable_anonymous_writes": False}, "1.5.0"), ({"guest_auth_policy_id": str(uuid.uuid4())}, "1.6.0"), + ({"delete_protected": False}, "1.8.0"), + # combining a long user_message (which uses callback-based detection) with + # higher and lower bounding fields needs to apply correctly + ( + {"force_verify": False, "user_message": "long message..." + "x" * 100}, + "1.7.0", + ), + ( + {"delete_protected": False, "user_message": "long message..." + "x" * 100}, + "1.8.0", + ), ], ) def test_datatype_version_deduction_mapped_specific_fields(use_kwargs, doc_version):