diff --git a/changelog.d/20260408_210055_pjhinton_sc_47448_transfer_v2_bookmark_sdk_support.rst b/changelog.d/20260408_210055_pjhinton_sc_47448_transfer_v2_bookmark_sdk_support.rst new file mode 100644 index 00000000..27b82955 --- /dev/null +++ b/changelog.d/20260408_210055_pjhinton_sc_47448_transfer_v2_bookmark_sdk_support.rst @@ -0,0 +1,4 @@ +Added +----- + +- Support for managing bookmarks via the Transfer v2 client (:pr:`NUMBER`) diff --git a/src/globus_sdk/experimental/__init__.py b/src/globus_sdk/experimental/__init__.py index 86215354..eb9b5411 100644 --- a/src/globus_sdk/experimental/__init__.py +++ b/src/globus_sdk/experimental/__init__.py @@ -3,6 +3,8 @@ import typing as t __all__ = ( + "BookmarkCreateDocument", + "BookmarkUpdateDocument", "TransferClientV2", "TunnelCreateDocument", "TunnelUpdateDocument", @@ -14,6 +16,8 @@ # components which do not need it if t.TYPE_CHECKING: from .transfer_v2 import ( + BookmarkCreateDocument, + BookmarkUpdateDocument, TransferClientV2, TunnelCreateDocument, TunnelUpdateDocument, @@ -21,6 +25,8 @@ else: _LAZY_IMPORT_TABLE = { "transfer_v2": { + "BookmarkCreateDocument", + "BookmarkUpdateDocument", "TransferClientV2", "TunnelCreateDocument", "TunnelUpdateDocument", diff --git a/src/globus_sdk/experimental/transfer_v2/__init__.py b/src/globus_sdk/experimental/transfer_v2/__init__.py index c23ddde5..6e12cf72 100644 --- a/src/globus_sdk/experimental/transfer_v2/__init__.py +++ b/src/globus_sdk/experimental/transfer_v2/__init__.py @@ -1,7 +1,14 @@ from .client import TransferClientV2 -from .data import TunnelCreateDocument, TunnelUpdateDocument +from .data import ( + BookmarkCreateDocument, + BookmarkUpdateDocument, + TunnelCreateDocument, + TunnelUpdateDocument, +) __all__ = ( + "BookmarkCreateDocument", + "BookmarkUpdateDocument", "TransferClientV2", "TunnelCreateDocument", "TunnelUpdateDocument", diff --git a/src/globus_sdk/experimental/transfer_v2/client.py b/src/globus_sdk/experimental/transfer_v2/client.py index 64160690..46796eac 100644 --- a/src/globus_sdk/experimental/transfer_v2/client.py +++ b/src/globus_sdk/experimental/transfer_v2/client.py @@ -10,7 +10,12 @@ from globus_sdk.services.transfer.errors import TransferAPIError from globus_sdk.transport import RetryConfig -from .data import TunnelCreateDocument, TunnelUpdateDocument +from .data import ( + BookmarkCreateDocument, + BookmarkUpdateDocument, + TunnelCreateDocument, + TunnelUpdateDocument, +) from .transport import TRANSFER_V2_DEFAULT_RETRY_CHECKS log = logging.getLogger(__name__) @@ -72,7 +77,7 @@ def create_tunnel( .. code-block:: python - tc = globus_sdk.experimental.TrasferClientV2(...) + tc = globus_sdk.experimental.TransferClientV2(...) data = globus_sdk.experimental.TunnelCreateDocument(...) result = tc.create_tunnel(data) print(result["data"]["id"]) @@ -81,7 +86,7 @@ def create_tunnel( ``POST /v2/tunnels`` """ - log.debug("TransferClientV2.create_tunnel(...)") + log.debug(f"{self.__class__.__name__}.create_tunnel(...)") r = self.post("/v2/tunnels", data=data) return r @@ -100,7 +105,7 @@ def update_tunnel( .. code-block:: python - tc = globus_sdk.experimental.TrasferClientV2(...) + tc = globus_sdk.experimental.TransferClientV2(...) data = globus_sdk.experimental.TunnelUpdateDocument(...) result = tc.update_tunnel(tunnel_id, data) print(result["data"]) @@ -109,7 +114,7 @@ def update_tunnel( ``PATCH /v2/tunnels/`` """ - log.debug(f"TransferClientV2.update_tunnel({tunnel_id}, {update_doc})") + log.debug(f"{self.__class__.__name__}.update_tunnel({tunnel_id}, {update_doc})") r = self.patch(f"/v2/tunnels/{tunnel_id}", data=update_doc) return r @@ -130,15 +135,15 @@ def get_tunnel( .. code-block:: python - tc = globus_sdk.experimental.TrasferClientV2(...) - result = tc.show_tunnel(tunnel_id) + tc = globus_sdk.experimental.TransferClientV2(...) + result = tc.get_tunnel(tunnel_id) print(result["data"]) .. tab-item:: API Info ``GET /v2/tunnels/`` """ - log.debug("TransferClientV2.get_tunnel({tunnel_id}, {query_params})") + log.debug(f"{self.__class__.__name__}.get_tunnel({tunnel_id}, {query_params})") r = self.get(f"/v2/tunnels/{tunnel_id}", query_params=query_params) return r @@ -158,14 +163,14 @@ def delete_tunnel( .. code-block:: python - tc = globus_sdk.experimental.TrasferClientV2(...) + tc = globus_sdk.experimental.TransferClientV2(...) tc.delete_tunnel(tunnel_id) .. tab-item:: API Info ``DELETE /v2/tunnels/`` """ - log.debug(f"TransferClientV2.delete_tunnel({tunnel_id})") + log.debug(f"{self.__class__.__name__}.delete_tunnel({tunnel_id})") r = self.delete(f"/v2/tunnels/{tunnel_id}") return r @@ -186,14 +191,14 @@ def list_tunnels( .. code-block:: python - tc = globus_sdk.experimental.TrasferClientV2(...) + tc = globus_sdk.experimental.TransferClientV2(...) tc.list_tunnels(tunnel_id) .. tab-item:: API Info ``GET /v2/tunnels/`` """ - log.debug(f"TransferClientV2.list_tunnels({query_params})") + log.debug(f"{self.__class__.__name__}.list_tunnels({query_params})") r = self.get("/v2/tunnels", query_params=query_params) return IterableJSONAPIResponse(r) @@ -214,7 +219,7 @@ def get_tunnel_events( .. code-block:: python - tc = globus_sdk.experimental.TrasferClientV2(...) + tc = globus_sdk.experimental.TransferClientV2(...) result = tc.get_tunnel_events(tunnel_id) print(result["data"]) @@ -222,7 +227,9 @@ def get_tunnel_events( ``GET /v2/tunnels//events`` """ - log.debug(f"TransferClientV2.get_tunnel_events({tunnel_id}, {query_params})") + log.debug( + f"{self.__class__.__name__}.get_tunnel_events({tunnel_id}, {query_params})" + ) r = self.get(f"/v2/tunnels/{tunnel_id}/events", query_params=query_params) return IterableJSONAPIResponse(r) @@ -247,7 +254,7 @@ def get_stream_access_point( .. code-block:: python - tc = globus_sdk.experimental.TrasferClientV2(...) + tc = globus_sdk.experimental.TransferClientV2(...) tc.get_stream_access_point(stream_ap_id) .. tab-item:: API Info @@ -255,7 +262,8 @@ def get_stream_access_point( ``GET /v2/stream_access_points/`` """ log.debug( - f"TransferClientV2.get_stream_access_point({stream_ap_id}, {query_params})" + f"{self.__class__.__name__}." + f"get_stream_access_point({stream_ap_id}, {query_params})" ) r = self.get( f"/v2/stream_access_points/{stream_ap_id}", query_params=query_params @@ -280,13 +288,157 @@ def list_stream_access_points( .. code-block:: python - tc = globus_sdk.experimental.TrasferClientV2(...) + tc = globus_sdk.experimental.TransferClientV2(...) tc.list_stream_access_points() .. tab-item:: API Info ``GET /v2/stream_access_points`` """ - log.debug(f"TransferClientV2.list_stream_access_points({query_params})") + log.debug( + f"{self.__class__.__name__}.list_stream_access_points({query_params})" + ) r = self.get("/v2/stream_access_points", query_params=query_params) return IterableJSONAPIResponse(r) + + def create_bookmark( + self, data: dict[str, t.Any] | BookmarkCreateDocument + ) -> response.GlobusHTTPResponse: + """ + :param data: Parameters for bookmark creation + + .. tab-set:: + + .. tab-item:: Example Usage + + .. code-block:: python + + tc = globus_sdk.experimental.TransferClientV2(...) + data = globus_sdk.experimental.BookmarkCreateDocument(...) + result = tc.create_bookmark(data) + print(result["data"]["id"]) + + .. tab-item:: API Info + + ``POST /v2/bookmarks`` + """ + log.debug(f"{self.__class__.__name__}.create_bookmark(...)") + r = self.post("/v2/bookmarks", data=data) + return r + + def update_bookmark( + self, + bookmark_id: uuid.UUID | str, + update_document: dict[str, t.Any] | BookmarkUpdateDocument, + ) -> response.GlobusHTTPResponse: + r""" + :param bookmark_id: The ID of the Bookmark. + :param update_document: The document that will be sent to the patch API. + + .. tab-set:: + + .. tab-item:: Example Usage + + .. code-block:: python + + tc = globus_sdk.experimental.TransferClientV2(...) + data = globus_sdk.experimental.BookmarkUpdateDocument(...) + result = tc.update_bookmark(bookmark_id, data) + print(result["data"]) + + .. tab-item:: API Info + + ``PATCH /v2/bookmarks/`` + """ + log.debug( + f"{self.__class__.__name__}." + f"update_bookmark({bookmark_id}, {update_document})" + ) + r = self.patch(f"/v2/bookmarks/{bookmark_id}", data=update_document) + return r + + def get_bookmark( + self, + bookmark_id: uuid.UUID | str, + *, + query_params: dict[str, t.Any] | None = None, + ) -> response.GlobusHTTPResponse: + """ + :param bookmark_id: The ID of the Bookmark for which we are fetching details. + :param query_params: Any additional parameters will be passed through + as request parameters on the URL. + + .. tab-set:: + + .. tab-item:: Example Usage + + .. code-block:: python + + tc = globus_sdk.experimental.TransferClientV2(...) + query_params = {"include": "collection"} + result = tc.get_bookmark(bookmark_id, query_params) + print(result["data"]) + + .. tab-item:: API Info + + ``GET /v2/bookmarks/`` + """ + log.debug( + f"{self.__class__.__name__}.get_bookmark({bookmark_id}, {query_params})" + ) + r = self.get(f"/v2/bookmarks/{bookmark_id}", query_params=query_params) + return r + + def delete_bookmark( + self, + bookmark_id: uuid.UUID | str, + ) -> response.GlobusHTTPResponse: + """ + :param bookmark_id: The ID of the Bookmark to be deleted. + + .. tab-set:: + + .. tab-item:: Example Usage + + .. code-block:: python + + tc = globus_sdk.experimental.TransferClientV2(...) + tc.delete_bookmark(bookmark_id) + + .. tab-item:: API Info + + ``DELETE /v2/bookmarks/`` + """ + log.debug(f"{self.__class__.__name__}.delete_bookmark({bookmark_id})") + r = self.delete(f"/v2/bookmarks/{bookmark_id}") + return r + + def list_bookmarks( + self, + *, + query_params: dict[str, t.Any] | None = None, + ) -> IterableJSONAPIResponse: + """ + :param query_params: Any additional parameters will be passed through + as request parameters on the URL. + + This will list all Bookmarks created by the authenticated user's primary + and linked identities. + + .. tab-set:: + + .. tab-item:: Example Usage + + .. code-block:: python + + tc = globus_sdk.experimental.TransferClientV2(...) + query_params = {"include": "collection"} + tc.list_bookmarks(query_params) + + .. tab-item:: API Info + + ``GET /v2/bookmarks`` + """ + log.debug(f"{self.__class__.__name__}.list_bookmarks({query_params})") + r = self.get("/v2/bookmarks", query_params=query_params) + return IterableJSONAPIResponse(r) diff --git a/src/globus_sdk/experimental/transfer_v2/data/__init__.py b/src/globus_sdk/experimental/transfer_v2/data/__init__.py index 798b1ecd..39a92506 100644 --- a/src/globus_sdk/experimental/transfer_v2/data/__init__.py +++ b/src/globus_sdk/experimental/transfer_v2/data/__init__.py @@ -4,6 +4,12 @@ :class:`TransferClient ` methods without conversion. """ +from .bookmark_documents import BookmarkCreateDocument, BookmarkUpdateDocument from .tunnel_documents import TunnelCreateDocument, TunnelUpdateDocument -__all__ = ("TunnelCreateDocument", "TunnelUpdateDocument") +__all__ = ( + "BookmarkCreateDocument", + "BookmarkUpdateDocument", + "TunnelCreateDocument", + "TunnelUpdateDocument", +) diff --git a/src/globus_sdk/experimental/transfer_v2/data/bookmark_documents.py b/src/globus_sdk/experimental/transfer_v2/data/bookmark_documents.py new file mode 100644 index 00000000..15949bd0 --- /dev/null +++ b/src/globus_sdk/experimental/transfer_v2/data/bookmark_documents.py @@ -0,0 +1,87 @@ +from __future__ import annotations + +import logging +import typing as t +import uuid + +from globus_sdk._missing import MISSING, MissingType +from globus_sdk._payload import GlobusPayload + +log = logging.getLogger(__name__) + + +class BookmarkCreateDocument(GlobusPayload): + """ + Convenience class for constructing a bookmark document to use as the + ``data`` parameter to + :meth:`~globus_sdk.TransferClientV2.create_bookmark`. + """ + + def __init__( + self, + collection: uuid.UUID | str, + name: str, + path: str, + *, + pinned: bool | MissingType = MISSING, + additional_fields: dict[str, t.Any] | None = None, + ) -> None: + super().__init__() + log.debug("Creating a new BookmarkCreateDocument object") + + relationships = { + "collection": { + "data": { + "type": "Collection", + "id": collection, + } + } + } + + attributes = { + "name": name, + "path": path, + "pinned": pinned, + } + + if additional_fields is not None: + attributes.update(additional_fields) + + self["data"] = { + "type": "Bookmark", + "attributes": attributes, + "relationships": relationships, + } + + +class BookmarkUpdateDocument(GlobusPayload): + """ + Convenience class for constructing a bookmark document to use as the + ``data`` parameter to + :meth:`~globus_sdk.TransferClientV2.update_bookmark`. + """ + + def __init__( + self, + name: str, + path: str, + *, + pinned: bool | MissingType = MISSING, + additional_fields: dict[str, t.Any] | None = None, + ) -> None: + super().__init__() + log.debug("Creating a new BookmarkUpdateDocument") + + attributes = { + "name": name, + "path": path, + "pinned": pinned, + } + + if additional_fields is not None: + attributes.update(additional_fields) + + self["data"] = { + "type": "Bookmark", + "attributes": attributes, + } diff --git a/src/globus_sdk/testing/data/transfer/v2/create_bookmark.py b/src/globus_sdk/testing/data/transfer/v2/create_bookmark.py new file mode 100644 index 00000000..bc6dc779 --- /dev/null +++ b/src/globus_sdk/testing/data/transfer/v2/create_bookmark.py @@ -0,0 +1,45 @@ +import uuid + +from globus_sdk.testing.models import RegisteredResponse, ResponseSet + +BOOKMARK_ID = str(uuid.uuid4()) + +_collection_id = str(uuid.uuid4()) +_name = "public datasets" +_path = "/data_repository/public" + + +RESPONSES = ResponseSet( + default=RegisteredResponse( + service="transfer", + method="POST", + path="/v2/bookmarks", + status=201, + json={ + "meta": {"request_id": "nDZoeEMPI"}, + "data": { + "type": "Bookmark", + "id": BOOKMARK_ID, + "attributes": { + "name": _name, + "path": _path, + "pinned": False, + }, + "relationships": { + "collection": { + "data": { + "type": "Collection", + "id": _collection_id, + } + } + }, + }, + }, + metadata={ + "bookmark_id": BOOKMARK_ID, + "collection": _collection_id, + "name": _name, + "path": _path, + }, + ) +) diff --git a/src/globus_sdk/testing/data/transfer/v2/delete_bookmark.py b/src/globus_sdk/testing/data/transfer/v2/delete_bookmark.py new file mode 100644 index 00000000..ecb26831 --- /dev/null +++ b/src/globus_sdk/testing/data/transfer/v2/delete_bookmark.py @@ -0,0 +1,19 @@ +import uuid + +from globus_sdk.testing.models import RegisteredResponse, ResponseSet + +BOOKMARK_ID = str(uuid.uuid4()) + + +RESPONSES = ResponseSet( + default=RegisteredResponse( + service="transfer", + method="DELETE", + path=f"/v2/bookmarks/{BOOKMARK_ID}", + status=200, + json={"meta": {"request_id": "nH45vUMpD"}}, + metadata={ + "bookmark_id": BOOKMARK_ID, + }, + ) +) diff --git a/src/globus_sdk/testing/data/transfer/v2/get_bookmark.py b/src/globus_sdk/testing/data/transfer/v2/get_bookmark.py new file mode 100644 index 00000000..9ad2aa08 --- /dev/null +++ b/src/globus_sdk/testing/data/transfer/v2/get_bookmark.py @@ -0,0 +1,50 @@ +import uuid + +from globus_sdk.testing.models import RegisteredResponse, ResponseSet + +BOOKMARK_ID = str(uuid.uuid4()) + +_collection_id = str(uuid.uuid4()) +_name = "public datasets" +_path = "/data_repository/public" + + +RESPONSES = ResponseSet( + default=RegisteredResponse( + service="transfer", + method="GET", + path=f"/v2/bookmarks/{BOOKMARK_ID}", + json={ + "meta": {"request_id": "HKsv2T65H"}, + "data": { + "type": "Bookmark", + "id": BOOKMARK_ID, + "attributes": {"name": _name, "path": _path, "pinned": True}, + "relationships": { + "collection": { + "data": { + "type": "Collection", + "id": _collection_id, + } + } + }, + }, + "included": [ + { + "type": "Collection", + "id": _collection_id, + "attributes": { + "display_name": "GCP High Assurance Mapped Collection", + "high_assurance": True, + }, + } + ], + }, + metadata={ + "bookmark_id": BOOKMARK_ID, + "collection": _collection_id, + "name": _name, + "path": _path, + }, + ) +) diff --git a/src/globus_sdk/testing/data/transfer/v2/list_bookmarks.py b/src/globus_sdk/testing/data/transfer/v2/list_bookmarks.py new file mode 100644 index 00000000..deddbdd5 --- /dev/null +++ b/src/globus_sdk/testing/data/transfer/v2/list_bookmarks.py @@ -0,0 +1,65 @@ +import uuid + +from globus_sdk.testing.models import RegisteredResponse, ResponseSet + +_bookmarks = [ + { + "bookmark_id": str(uuid.uuid4()), + "collection_id": str(uuid.uuid4()), + "name": "public datasets", + "path": "/data_repository/public", + "pinned": True, + }, + { + "bookmark_id": str(uuid.uuid4()), + "collection_id": str(uuid.uuid4()), + "name": "private datasets", + "path": "/data_repository/private", + "pinned": False, + }, +] + +RESPONSES = ResponseSet( + default=RegisteredResponse( + service="transfer", + method="GET", + path="/v2/bookmarks", + json={ + "data": [ + { + "type": "Bookmark", + "id": b["bookmark_id"], + "attributes": { + "name": b["name"], + "path": b["path"], + "pinned": b["pinned"], + }, + "relationships": { + "collection": { + "data": { + "type": "Collection", + "id": b["collection_id"], + } + } + }, + } + for b in _bookmarks + ], + "meta": { + "request_id": "xj8AWyQr8", + }, + "included": [ + { + "type": "Collection", + "id": b["collection_id"], + "attributes": { + "display_name": "Wotsomatta U Dataset Collection", + "high_assurance": False, + }, + } + for b in _bookmarks + ], + }, + ), + metadata={"bookmarks": _bookmarks}, +) diff --git a/src/globus_sdk/testing/data/transfer/v2/update_bookmark.py b/src/globus_sdk/testing/data/transfer/v2/update_bookmark.py new file mode 100644 index 00000000..62a81ebd --- /dev/null +++ b/src/globus_sdk/testing/data/transfer/v2/update_bookmark.py @@ -0,0 +1,47 @@ +import uuid + +from globus_sdk.testing.models import RegisteredResponse, ResponseSet + +BOOKMARK_ID = str(uuid.uuid4()) + +_collection_id = str(uuid.uuid4()) +_name = "private datasets" +_path = "/data_repository/private" +_pinned = True + + +RESPONSES = ResponseSet( + default=RegisteredResponse( + service="transfer", + method="PATCH", + path=f"/v2/bookmarks/{BOOKMARK_ID}", + status=200, + json={ + "meta": {"request_id": "dDaKboC8I"}, + "data": { + "type": "Bookmark", + "id": BOOKMARK_ID, + "attributes": { + "name": _name, + "path": _path, + "pinned": _pinned, + }, + "relationships": { + "collection": { + "data": { + "type": "Collection", + "id": _collection_id, + } + } + }, + }, + }, + metadata={ + "bookmark_id": BOOKMARK_ID, + "collection": _collection_id, + "name": _name, + "path": _path, + "pinned": _pinned, + }, + ) +) diff --git a/tests/functional/services/transfer/v2/test_create_bookmark.py b/tests/functional/services/transfer/v2/test_create_bookmark.py new file mode 100644 index 00000000..9a33c272 --- /dev/null +++ b/tests/functional/services/transfer/v2/test_create_bookmark.py @@ -0,0 +1,27 @@ +import json + +from globus_sdk.experimental import BookmarkCreateDocument +from globus_sdk.testing import get_last_request, load_response + + +def test_create_bookmark(client): + meta = load_response(client.create_bookmark).metadata + + data = BookmarkCreateDocument( + meta["collection"], + meta["name"], + meta["path"], + ) + + res = client.create_bookmark(data) + + assert res.http_status == 201 + assert res["data"]["type"] == "Bookmark" + + req = get_last_request() + sent = json.loads(req.body) + assert sent["data"]["attributes"]["name"] == meta["name"] + assert sent["data"]["attributes"]["path"] == meta["path"] + assert ( + sent["data"]["relationships"]["collection"]["data"]["id"] == meta["collection"] + ) diff --git a/tests/functional/services/transfer/v2/test_delete_bookmark.py b/tests/functional/services/transfer/v2/test_delete_bookmark.py new file mode 100644 index 00000000..19d9c3f0 --- /dev/null +++ b/tests/functional/services/transfer/v2/test_delete_bookmark.py @@ -0,0 +1,12 @@ +from globus_sdk.testing import get_last_request, load_response + + +def test_delete_bookmark(client): + meta = load_response(client.delete_bookmark).metadata + + res = client.delete_bookmark(meta["bookmark_id"]) + + assert res.http_status == 200 + + req = get_last_request() + assert req.body is None diff --git a/tests/functional/services/transfer/v2/test_get_bookmark.py b/tests/functional/services/transfer/v2/test_get_bookmark.py new file mode 100644 index 00000000..0ea5d1d6 --- /dev/null +++ b/tests/functional/services/transfer/v2/test_get_bookmark.py @@ -0,0 +1,20 @@ +from globus_sdk.testing import get_last_request, load_response + + +def test_get_bookmark(client): + meta = load_response(client.get_bookmark).metadata + + res = client.get_bookmark( + meta["bookmark_id"], query_params={"include": "collection"} + ) + + assert res.http_status == 200 + assert res["data"]["type"] == "Bookmark" + assert res["data"]["id"] == meta["bookmark_id"] + assert ( + res["data"]["relationships"]["collection"]["data"]["id"] == meta["collection"] + ) + + req = get_last_request() + assert "include=collection" in req.url + assert req.body is None diff --git a/tests/functional/services/transfer/v2/test_list_bookmarks.py b/tests/functional/services/transfer/v2/test_list_bookmarks.py new file mode 100644 index 00000000..7e094875 --- /dev/null +++ b/tests/functional/services/transfer/v2/test_list_bookmarks.py @@ -0,0 +1,30 @@ +from globus_sdk.testing import get_last_request, load_response + + +def test_list_bookmarks(client): + meta = load_response(client.list_bookmarks).metadata + + res = client.list_bookmarks( + query_params={"include": "collection"}, + ) + + assert res.http_status == 200 + for i, bm in enumerate(res["data"]): + assert bm["type"] == "Bookmark" + assert bm["id"] == meta["bookmarks"][i]["bookmark_id"] + assert bm["attributes"]["name"] == meta["bookmarks"][i]["name"] + assert bm["attributes"]["path"] == meta["bookmarks"][i]["path"] + assert bm["attributes"]["pinned"] == meta["bookmarks"][i]["pinned"] + + assert ( + bm["relationships"]["collection"]["data"]["id"] + == meta["bookmarks"][i]["collection_id"] + ) + + for i, col in enumerate(res["included"]): + assert col["type"] == "Collection" + assert col["id"] == meta["bookmarks"][i]["collection_id"] + + req = get_last_request() + assert "include=collection" in req.url + assert req.body is None diff --git a/tests/functional/services/transfer/v2/test_update_bookmark.py b/tests/functional/services/transfer/v2/test_update_bookmark.py new file mode 100644 index 00000000..5f30d296 --- /dev/null +++ b/tests/functional/services/transfer/v2/test_update_bookmark.py @@ -0,0 +1,28 @@ +import json + +from globus_sdk.experimental import BookmarkUpdateDocument +from globus_sdk.testing import get_last_request, load_response + + +def test_update_bookmark(client): + meta = load_response(client.update_bookmark).metadata + + data = BookmarkUpdateDocument( + meta["name"], + meta["path"], + pinned=meta["pinned"], + ) + + res = client.update_bookmark(meta["bookmark_id"], data) + + assert res.http_status == 200 + assert res["data"]["type"] == "Bookmark" + assert ( + res["data"]["relationships"]["collection"]["data"]["id"] == meta["collection"] + ) + + req = get_last_request() + sent = json.loads(req.body) + assert sent["data"]["attributes"]["name"] == meta["name"] + assert sent["data"]["attributes"]["path"] == meta["path"] + assert sent["data"]["attributes"]["pinned"] == meta["pinned"]