summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNejc Habjan <nejc.habjan@siemens.com>2022-07-24 22:32:31 +0200
committerJohn Villalovos <john@sodarock.com>2022-07-26 22:41:59 -0700
commit66461ba519a85bfbd3cba284a0c8de11a3ac7cde (patch)
tree853f37fb1ca26aea5339f82b9c578469d1fde401
parent8d4f13b192afd5d4610eeaf2bbea71c3b6a25964 (diff)
downloadgitlab-66461ba519a85bfbd3cba284a0c8de11a3ac7cde.tar.gz
feat(groups): add support for shared projects API
-rw-r--r--docs/gl_objects/groups.rst10
-rw-r--r--gitlab/v4/objects/groups.py3
-rw-r--r--gitlab/v4/objects/projects.py25
-rw-r--r--tests/unit/objects/test_groups.py38
4 files changed, 72 insertions, 4 deletions
diff --git a/docs/gl_objects/groups.rst b/docs/gl_objects/groups.rst
index b3eb332..71e955f 100644
--- a/docs/gl_objects/groups.rst
+++ b/docs/gl_objects/groups.rst
@@ -31,11 +31,15 @@ List a group's projects::
projects = group.projects.list()
+List a group's shared projects::
+
+ projects = group.shared_projects.list()
+
.. note::
- ``GroupProject`` objects returned by this API call are very limited, and do
- not provide all the features of ``Project`` objects. If you need to
- manipulate projects, create a new ``Project`` object::
+ ``GroupProject`` and ``SharedProject`` objects returned by these two API calls
+ are very limited, and do not provide all the features of ``Project`` objects.
+ If you need to manipulate projects, create a new ``Project`` object::
first_group_project = group.projects.list()[0]
manageable_project = gl.projects.get(first_group_project.id, lazy=True)
diff --git a/gitlab/v4/objects/groups.py b/gitlab/v4/objects/groups.py
index 45a4dad..5b35e00 100644
--- a/gitlab/v4/objects/groups.py
+++ b/gitlab/v4/objects/groups.py
@@ -34,7 +34,7 @@ from .merge_requests import GroupMergeRequestManager # noqa: F401
from .milestones import GroupMilestoneManager # noqa: F401
from .notification_settings import GroupNotificationSettingsManager # noqa: F401
from .packages import GroupPackageManager # noqa: F401
-from .projects import GroupProjectManager # noqa: F401
+from .projects import GroupProjectManager, SharedProjectManager # noqa: F401
from .push_rules import GroupPushRulesManager
from .runners import GroupRunnerManager # noqa: F401
from .statistics import GroupIssuesStatisticsManager # noqa: F401
@@ -79,6 +79,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
notificationsettings: GroupNotificationSettingsManager
packages: GroupPackageManager
projects: GroupProjectManager
+ shared_projects: SharedProjectManager
pushrules: GroupPushRulesManager
registry_repositories: GroupRegistryRepositoryManager
runners: GroupRunnerManager
diff --git a/gitlab/v4/objects/projects.py b/gitlab/v4/objects/projects.py
index b008dda..e46e53d 100644
--- a/gitlab/v4/objects/projects.py
+++ b/gitlab/v4/objects/projects.py
@@ -103,6 +103,8 @@ __all__ = [
"ProjectRemoteMirrorManager",
"ProjectStorage",
"ProjectStorageManager",
+ "SharedProject",
+ "SharedProjectManager",
]
@@ -1081,3 +1083,26 @@ class ProjectStorageManager(GetWithoutIdMixin, RESTManager):
def get(self, **kwargs: Any) -> ProjectStorage:
return cast(ProjectStorage, super().get(**kwargs))
+
+
+class SharedProject(RESTObject):
+ pass
+
+
+class SharedProjectManager(ListMixin, RESTManager):
+ _path = "/groups/{group_id}/projects/shared"
+ _obj_cls = SharedProject
+ _from_parent_attrs = {"group_id": "id"}
+ _list_filters = (
+ "archived",
+ "visibility",
+ "order_by",
+ "sort",
+ "search",
+ "simple",
+ "starred",
+ "with_issues_enabled",
+ "with_merge_requests_enabled",
+ "min_access_level",
+ "with_custom_attributes",
+ )
diff --git a/tests/unit/objects/test_groups.py b/tests/unit/objects/test_groups.py
index cebdfc7..6ca5cfe 100644
--- a/tests/unit/objects/test_groups.py
+++ b/tests/unit/objects/test_groups.py
@@ -9,8 +9,21 @@ import responses
import gitlab
from gitlab.v4.objects import GroupDescendantGroup, GroupSubgroup
+from gitlab.v4.objects.projects import GroupProject, SharedProject
content = {"name": "name", "id": 1, "path": "path"}
+projects_content = [
+ {
+ "id": 9,
+ "description": "foo",
+ "default_branch": "master",
+ "name": "Html5 Boilerplate",
+ "name_with_namespace": "Experimental / Html5 Boilerplate",
+ "path": "html5-boilerplate",
+ "path_with_namespace": "h5bp/html5-boilerplate",
+ "namespace": {"id": 5, "name": "Experimental", "path": "h5bp", "kind": "group"},
+ }
+]
subgroup_descgroup_content = [
{
"id": 2,
@@ -81,6 +94,19 @@ def resp_groups():
@pytest.fixture
+def resp_list_group_projects():
+ with responses.RequestsMock() as rsps:
+ rsps.add(
+ method=responses.GET,
+ url=re.compile(r"http://localhost/api/v4/groups/1/projects(/shared)?"),
+ json=projects_content,
+ content_type="application/json",
+ status=200,
+ )
+ yield rsps
+
+
+@pytest.fixture
def resp_list_subgroups_descendant_groups():
with responses.RequestsMock() as rsps:
rsps.add(
@@ -211,6 +237,18 @@ def test_create_group_export(group, resp_export):
assert export.message == "202 Accepted"
+def test_list_group_projects(group, resp_list_group_projects):
+ projects = group.projects.list()
+ assert isinstance(projects[0], GroupProject)
+ assert projects[0].path == projects_content[0]["path"]
+
+
+def test_list_group_shared_projects(group, resp_list_group_projects):
+ projects = group.shared_projects.list()
+ assert isinstance(projects[0], SharedProject)
+ assert projects[0].path == projects_content[0]["path"]
+
+
def test_list_group_subgroups(group, resp_list_subgroups_descendant_groups):
subgroups = group.subgroups.list()
assert isinstance(subgroups[0], GroupSubgroup)