From 754cc9011d5cda316292b8657f460102a4d95ea6 Mon Sep 17 00:00:00 2001 From: Jonathan Keelan Date: Thu, 30 Apr 2020 20:13:20 +0100 Subject: [PATCH 1/5] add move to object client --- faculty/clients/object.py | 31 +++++++++++++++++++++++++++++++ tests/clients/test_object.py | 22 ++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/faculty/clients/object.py b/faculty/clients/object.py index 2b220bf6..65ce4f80 100644 --- a/faculty/clients/object.py +++ b/faculty/clients/object.py @@ -239,6 +239,37 @@ def copy(self, project_id, source, destination, recursive=False): else: raise + def move(self, project_id, source, destination): + """Move objects in the store + + Parameters + ---------- + project_id : uuid.UUID + The project to copy objects in. + source : str + Move object from this source path. + destination : str + Move object to this destination path. + + + Raises + ------ + PathNotFound + When the source path does not exist or is not found. + """ + url_encoded_destination = urllib.parse.quote(destination.lstrip("/")) + + endpoint = "/project/{}/object-move/{}".format( + project_id, url_encoded_destination + ) + params = {"sourcePath": source} + + try: + self._put_raw(endpoint, params=params) + except NotFound as err: + if err.error_code == "source_path_not_found": + raise PathNotFound(source) + def delete(self, project_id, path, recursive=False): """Delete objects in the store. diff --git a/tests/clients/test_object.py b/tests/clients/test_object.py index 7afbd0a6..ca6251ac 100644 --- a/tests/clients/test_object.py +++ b/tests/clients/test_object.py @@ -378,6 +378,28 @@ def test_object_client_copy_source_is_a_directory(mocker): client.copy(PROJECT_ID, "source", "destination") +def test_object_client_move_url_encoding(mocker): + mocker.patch.object(ObjectClient, "_put_raw") + + client = ObjectClient(mocker.Mock()) + client.move(PROJECT_ID, "source", "/[1]/") + + ObjectClient._put_raw.assert_called_once_with( + "/project/{}/object-move/%5B1%5D/".format(PROJECT_ID), + params={"sourcePath": "source"}, + ) + + +def test_object_client_move_source_not_found(mocker): + error_code = "source_path_not_found" + exception = NotFound(mocker.Mock(), mocker.Mock(), error_code) + mocker.patch.object(ObjectClient, "_put_raw", side_effect=exception) + + client = ObjectClient(mocker.Mock()) + with pytest.raises(PathNotFound, match="'source' cannot be found"): + client.move(PROJECT_ID, "source", "destination") + + def test_object_client_delete_default(mocker): path = "test-path" mocker.patch.object(ObjectClient, "_delete_raw") From 9fedc2d4c5684df51006afd5844a19df31234b94 Mon Sep 17 00:00:00 2001 From: Jonathan Keelan Date: Thu, 30 Apr 2020 20:25:13 +0100 Subject: [PATCH 2/5] change datasets.mv to use move, not cp rm --- faculty/datasets/__init__.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/faculty/datasets/__init__.py b/faculty/datasets/__init__.py index 26ba5e3d..ebe52d25 100644 --- a/faculty/datasets/__init__.py +++ b/faculty/datasets/__init__.py @@ -334,19 +334,7 @@ def mv(source_path, destination_path, project_id=None, object_client=None): project_id = project_id or get_context().project_id object_client = object_client or ObjectClient(get_session()) - cp( - source_path, - destination_path, - project_id=project_id, - recursive=True, - object_client=object_client, - ) - rm( - source_path, - project_id=project_id, - recursive=True, - object_client=object_client, - ) + object_client.move(project_id, source_path, destination_path) def cp( From a3edb233017922bf22c705ddd75a5c3e485c2837 Mon Sep 17 00:00:00 2001 From: Jonathan Keelan Date: Thu, 30 Apr 2020 20:33:18 +0100 Subject: [PATCH 3/5] change test for dataset.mv --- tests/datasets/test_init.py | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/tests/datasets/test_init.py b/tests/datasets/test_init.py index 181895c0..0c4436bf 100644 --- a/tests/datasets/test_init.py +++ b/tests/datasets/test_init.py @@ -419,23 +419,9 @@ def test_rmdir_nonempty_directory(mocker, prefix): def test_mv(mocker, mock_client): - cp_mock = mocker.patch("faculty.datasets.cp") - rm_mock = mocker.patch("faculty.datasets.rm") - datasets.mv("source-path", "destination-path", project_id=PROJECT_ID) - - cp_mock.assert_called_once_with( - "source-path", - "destination-path", - project_id=PROJECT_ID, - recursive=True, - object_client=mock_client, - ) - rm_mock.assert_called_once_with( - "source-path", - project_id=PROJECT_ID, - recursive=True, - object_client=mock_client, + mock_client.move.assert_called_once_with( + PROJECT_ID, "source-path", "destination-path", ) From f06fc92f583a00b99644347ce88aa56c8bd0a58c Mon Sep 17 00:00:00 2001 From: Jonathan Keelan Date: Thu, 30 Apr 2020 20:35:34 +0100 Subject: [PATCH 4/5] doc string for object client move --- faculty/clients/object.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/faculty/clients/object.py b/faculty/clients/object.py index 65ce4f80..099c0ff5 100644 --- a/faculty/clients/object.py +++ b/faculty/clients/object.py @@ -245,7 +245,7 @@ def move(self, project_id, source, destination): Parameters ---------- project_id : uuid.UUID - The project to copy objects in. + The project to move objects in. source : str Move object from this source path. destination : str From 5dcf4baddb5cf22fc72a489b4d25fe27e4fb42db Mon Sep 17 00:00:00 2001 From: Jonathan Keelan Date: Thu, 30 Apr 2020 20:39:43 +0100 Subject: [PATCH 5/5] black --- tests/datasets/test_init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/datasets/test_init.py b/tests/datasets/test_init.py index 0c4436bf..0368fe47 100644 --- a/tests/datasets/test_init.py +++ b/tests/datasets/test_init.py @@ -421,7 +421,7 @@ def test_rmdir_nonempty_directory(mocker, prefix): def test_mv(mocker, mock_client): datasets.mv("source-path", "destination-path", project_id=PROJECT_ID) mock_client.move.assert_called_once_with( - PROJECT_ID, "source-path", "destination-path", + PROJECT_ID, "source-path", "destination-path" )