Skip to content

Commit 66461ba

Browse files
nejchJohnVillalovos
authored andcommitted
feat(groups): add support for shared projects API
1 parent 8d4f13b commit 66461ba

File tree

4 files changed

+72
-4
lines changed

4 files changed

+72
-4
lines changed

docs/gl_objects/groups.rst

+7-3
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,15 @@ List a group's projects::
3131

3232
projects = group.projects.list()
3333

34+
List a group's shared projects::
35+
36+
projects = group.shared_projects.list()
37+
3438
.. note::
3539

36-
``GroupProject`` objects returned by this API call are very limited, and do
37-
not provide all the features of ``Project`` objects. If you need to
38-
manipulate projects, create a new ``Project`` object::
40+
``GroupProject`` and ``SharedProject`` objects returned by these two API calls
41+
are very limited, and do not provide all the features of ``Project`` objects.
42+
If you need to manipulate projects, create a new ``Project`` object::
3943

4044
first_group_project = group.projects.list()[0]
4145
manageable_project = gl.projects.get(first_group_project.id, lazy=True)

gitlab/v4/objects/groups.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from .milestones import GroupMilestoneManager # noqa: F401
3535
from .notification_settings import GroupNotificationSettingsManager # noqa: F401
3636
from .packages import GroupPackageManager # noqa: F401
37-
from .projects import GroupProjectManager # noqa: F401
37+
from .projects import GroupProjectManager, SharedProjectManager # noqa: F401
3838
from .push_rules import GroupPushRulesManager
3939
from .runners import GroupRunnerManager # noqa: F401
4040
from .statistics import GroupIssuesStatisticsManager # noqa: F401
@@ -79,6 +79,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
7979
notificationsettings: GroupNotificationSettingsManager
8080
packages: GroupPackageManager
8181
projects: GroupProjectManager
82+
shared_projects: SharedProjectManager
8283
pushrules: GroupPushRulesManager
8384
registry_repositories: GroupRegistryRepositoryManager
8485
runners: GroupRunnerManager

gitlab/v4/objects/projects.py

+25
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@
103103
"ProjectRemoteMirrorManager",
104104
"ProjectStorage",
105105
"ProjectStorageManager",
106+
"SharedProject",
107+
"SharedProjectManager",
106108
]
107109

108110

@@ -1081,3 +1083,26 @@ class ProjectStorageManager(GetWithoutIdMixin, RESTManager):
10811083

10821084
def get(self, **kwargs: Any) -> ProjectStorage:
10831085
return cast(ProjectStorage, super().get(**kwargs))
1086+
1087+
1088+
class SharedProject(RESTObject):
1089+
pass
1090+
1091+
1092+
class SharedProjectManager(ListMixin, RESTManager):
1093+
_path = "/groups/{group_id}/projects/shared"
1094+
_obj_cls = SharedProject
1095+
_from_parent_attrs = {"group_id": "id"}
1096+
_list_filters = (
1097+
"archived",
1098+
"visibility",
1099+
"order_by",
1100+
"sort",
1101+
"search",
1102+
"simple",
1103+
"starred",
1104+
"with_issues_enabled",
1105+
"with_merge_requests_enabled",
1106+
"min_access_level",
1107+
"with_custom_attributes",
1108+
)

tests/unit/objects/test_groups.py

+38
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,21 @@
99

1010
import gitlab
1111
from gitlab.v4.objects import GroupDescendantGroup, GroupSubgroup
12+
from gitlab.v4.objects.projects import GroupProject, SharedProject
1213

1314
content = {"name": "name", "id": 1, "path": "path"}
15+
projects_content = [
16+
{
17+
"id": 9,
18+
"description": "foo",
19+
"default_branch": "master",
20+
"name": "Html5 Boilerplate",
21+
"name_with_namespace": "Experimental / Html5 Boilerplate",
22+
"path": "html5-boilerplate",
23+
"path_with_namespace": "h5bp/html5-boilerplate",
24+
"namespace": {"id": 5, "name": "Experimental", "path": "h5bp", "kind": "group"},
25+
}
26+
]
1427
subgroup_descgroup_content = [
1528
{
1629
"id": 2,
@@ -80,6 +93,19 @@ def resp_groups():
8093
yield rsps
8194

8295

96+
@pytest.fixture
97+
def resp_list_group_projects():
98+
with responses.RequestsMock() as rsps:
99+
rsps.add(
100+
method=responses.GET,
101+
url=re.compile(r"http://localhost/api/v4/groups/1/projects(/shared)?"),
102+
json=projects_content,
103+
content_type="application/json",
104+
status=200,
105+
)
106+
yield rsps
107+
108+
83109
@pytest.fixture
84110
def resp_list_subgroups_descendant_groups():
85111
with responses.RequestsMock() as rsps:
@@ -211,6 +237,18 @@ def test_create_group_export(group, resp_export):
211237
assert export.message == "202 Accepted"
212238

213239

240+
def test_list_group_projects(group, resp_list_group_projects):
241+
projects = group.projects.list()
242+
assert isinstance(projects[0], GroupProject)
243+
assert projects[0].path == projects_content[0]["path"]
244+
245+
246+
def test_list_group_shared_projects(group, resp_list_group_projects):
247+
projects = group.shared_projects.list()
248+
assert isinstance(projects[0], SharedProject)
249+
assert projects[0].path == projects_content[0]["path"]
250+
251+
214252
def test_list_group_subgroups(group, resp_list_subgroups_descendant_groups):
215253
subgroups = group.subgroups.list()
216254
assert isinstance(subgroups[0], GroupSubgroup)

0 commit comments

Comments
 (0)