diff --git a/gitlab/v4/objects/projects.py b/gitlab/v4/objects/projects.py index 58666ce74..1a765d1ac 100644 --- a/gitlab/v4/objects/projects.py +++ b/gitlab/v4/objects/projects.py @@ -1,3 +1,4 @@ +import warnings from typing import Any, Callable, cast, Dict, List, Optional, TYPE_CHECKING, Union import requests @@ -526,7 +527,7 @@ def mirror_pull(self, **kwargs: Any) -> None: @cli.register_custom_action("Project", ("to_namespace",)) @exc.on_http_error(exc.GitlabTransferProjectError) - def transfer_project(self, to_namespace: str, **kwargs: Any) -> None: + def transfer(self, to_namespace: Union[int, str], **kwargs: Any) -> None: """Transfer a project to the given namespace ID Args: @@ -543,6 +544,15 @@ def transfer_project(self, to_namespace: str, **kwargs: Any) -> None: path, post_data={"namespace": to_namespace}, **kwargs ) + @cli.register_custom_action("Project", ("to_namespace",)) + def transfer_project(self, *args: Any, **kwargs: Any) -> None: + warnings.warn( + "The project.transfer_project() method is deprecated and will be " + "removed in a future version. Use project.transfer() instead.", + DeprecationWarning, + ) + return self.transfer(*args, **kwargs) + @cli.register_custom_action("Project", ("ref_name", "job"), ("job_token",)) @exc.on_http_error(exc.GitlabGetError) def artifacts( diff --git a/tests/functional/api/test_groups.py b/tests/functional/api/test_groups.py index 105acbb7f..584ea8355 100644 --- a/tests/functional/api/test_groups.py +++ b/tests/functional/api/test_groups.py @@ -231,3 +231,19 @@ def test_group_hooks(group): hook = group.hooks.get(hook.id) assert hook.note_events is True hook.delete() + + +@pytest.mark.skip(reason="Pending #1807") +def test_group_transfer(gl, group): + transfer_group = gl.groups.create({"name": "transfer-test-group"}) + assert group.namespace["path"] != group.full_path + + transfer_group.transfer(group.id) + + transferred_group = gl.projects.get(transfer_group.id) + assert transferred_group.namespace["path"] == group.full_path + + transfer_group.transfer() + + transferred_group = gl.projects.get(transfer_group.id) + assert transferred_group.path == transferred_group.full_path diff --git a/tests/functional/api/test_projects.py b/tests/functional/api/test_projects.py index 4cd951502..b4514e6dc 100644 --- a/tests/functional/api/test_projects.py +++ b/tests/functional/api/test_projects.py @@ -329,3 +329,17 @@ def test_project_groups_list(gl, group): groups = project.groups.list() group_ids = set([x.id for x in groups]) assert set((group.id, group2.id)) == group_ids + + +def test_project_transfer(gl, project, group): + assert project.namespace["path"] != group.full_path + project.transfer_project(group.id) + + project = gl.projects.get(project.id) + assert project.namespace["path"] == group.full_path + + gl.auth() + project.transfer_project(gl.user.username) + + project = gl.projects.get(project.id) + assert project.namespace["path"] == gl.user.username diff --git a/tests/unit/objects/test_groups.py b/tests/unit/objects/test_groups.py index 37023d8e3..b3e753e4b 100644 --- a/tests/unit/objects/test_groups.py +++ b/tests/unit/objects/test_groups.py @@ -10,6 +10,7 @@ import gitlab from gitlab.v4.objects import GroupDescendantGroup, GroupSubgroup +content = {"name": "name", "id": 1, "path": "path"} subgroup_descgroup_content = [ { "id": 2, @@ -41,8 +42,6 @@ @pytest.fixture def resp_groups(): - content = {"name": "name", "id": 1, "path": "path"} - with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps: rsps.add( method=responses.GET, @@ -96,6 +95,22 @@ def resp_create_import(accepted_content): yield rsps +@pytest.fixture +def resp_transfer_group(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.PUT, + url="http://localhost/api/v4/groups/1/transfer", + json=content, + content_type="application/json", + status=200, + match=[ + responses.matchers.json_params_matcher({"namespace": "test-namespace"}) + ], + ) + yield rsps + + def test_get_group(gl, resp_groups): data = gl.groups.get(1) assert isinstance(data, gitlab.v4.objects.Group) @@ -153,3 +168,9 @@ def test_refresh_group_import_status(group, resp_groups): group_import = group.imports.get() group_import.refresh() assert group_import.import_status == "finished" + + +@pytest.mark.skip("Pending #1807") +def test_transfer_group(gl, resp_transfer_group): + group = gl.groups.get(1, lazy=True) + group.transfer("test-namespace") diff --git a/tests/unit/objects/test_projects.py b/tests/unit/objects/test_projects.py index 039d5ec75..60693dec8 100644 --- a/tests/unit/objects/test_projects.py +++ b/tests/unit/objects/test_projects.py @@ -54,6 +54,22 @@ def resp_import_bitbucket_server(): yield rsps +@pytest.fixture +def resp_transfer_project(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.PUT, + url="http://localhost/api/v4/projects/1/transfer", + json=project_content, + content_type="application/json", + status=200, + match=[ + responses.matchers.json_params_matcher({"namespace": "test-namespace"}) + ], + ) + yield rsps + + def test_get_project(gl, resp_get_project): data = gl.projects.get(1) assert isinstance(data, Project) @@ -217,9 +233,15 @@ def test_delete_project_push_rule(gl): pass -@pytest.mark.skip(reason="missing test") -def test_transfer_project(gl): - pass +def test_transfer_project(gl, resp_transfer_project): + project = gl.projects.get(1, lazy=True) + project.transfer("test-namespace") + + +def test_transfer_project_deprecated_warns(gl, resp_transfer_project): + project = gl.projects.get(1, lazy=True) + with pytest.warns(DeprecationWarning): + project.transfer_project("test-namespace") @pytest.mark.skip(reason="missing test")