Skip to content

Commit aba713a

Browse files
author
Gauvain Pocentek
committed
Add support for group milestones
Closes #349
1 parent cf6767c commit aba713a

File tree

4 files changed

+106
-12
lines changed

4 files changed

+106
-12
lines changed

docs/gl_objects/milestones.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
# list
2-
milestones = project.milestones.list()
2+
p_milestones = project.milestones.list()
3+
g_milestones = group.milestones.list()
34
# end list
45

56
# filter
6-
milestones = project.milestones.list(state='closed')
7+
p_milestones = project.milestones.list(state='closed')
8+
g_milestones = group.milestones.list(state='active')
79
# end filter
810

911
# get
10-
milestone = project.milestones.get(milestone_id)
12+
p_milestone = project.milestones.get(milestone_id)
13+
g_milestone = group.milestones.get(milestone_id)
1114
# end get
1215

1316
# create
@@ -36,4 +39,3 @@
3639
# merge_requests
3740
merge_requests = milestone.merge_requests()
3841
# end merge_requests
39-

docs/gl_objects/milestones.rst

+10-2
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,26 @@ Reference
1111
+ :class:`gitlab.v4.objects.ProjectMilestoneManager`
1212
+ :attr:`gitlab.v4.objects.Project.milestones`
1313

14+
+ :class:`gitlab.v4.objects.GroupMilestone`
15+
+ :class:`gitlab.v4.objects.GroupMilestoneManager`
16+
+ :attr:`gitlab.v4.objects.Group.milestones`
17+
1418
* v3 API:
1519

1620
+ :class:`gitlab.v3.objects.ProjectMilestone`
1721
+ :class:`gitlab.v3.objects.ProjectMilestoneManager`
1822
+ :attr:`gitlab.v3.objects.Project.milestones`
1923
+ :attr:`gitlab.Gitlab.project_milestones`
2024

21-
* GitLab API: https://docs.gitlab.com/ce/api/milestones.html
25+
* GitLab API:
26+
27+
+ https://docs.gitlab.com/ce/api/milestones.html
28+
+ https://docs.gitlab.com/ce/api/group_milestones.html
2229

2330
Examples
2431
--------
2532

26-
List the milestones for a project:
33+
List the milestones for a project or a group:
2734

2835
.. literalinclude:: milestones.py
2936
:start-after: # list
@@ -33,6 +40,7 @@ You can filter the list using the following parameters:
3340

3441
* ``iid``: unique ID of the milestone for the project
3542
* ``state``: either ``active`` or ``closed``
43+
* ``search``: to search using a string
3644

3745
.. literalinclude:: milestones.py
3846
:start-after: # filter

gitlab/v4/objects.py

+75-5
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,75 @@ class GroupMemberManager(GetFromListMixin, CreateMixin, UpdateMixin,
395395
_update_attrs = (('access_level', ), ('expires_at', ))
396396

397397

398+
class GroupMergeRequest(RESTObject):
399+
pass
400+
401+
402+
class GroupMergeRequestManager(RESTManager):
403+
pass
404+
405+
406+
class GroupMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
407+
_short_print_attr = 'title'
408+
409+
@cli.register_custom_action('GroupMilestone')
410+
@exc.on_http_error(exc.GitlabListError)
411+
def issues(self, **kwargs):
412+
"""List issues related to this milestone.
413+
414+
Args:
415+
**kwargs: Extra options to send to the server (e.g. sudo)
416+
417+
Raises:
418+
GitlabAuthenticationError: If authentication is not correct
419+
GitlabListError: If the list could not be retrieved
420+
421+
Returns:
422+
RESTObjectList: The list of issues
423+
"""
424+
425+
path = '%s/%s/issues' % (self.manager.path, self.get_id())
426+
data_list = self.manager.gitlab.http_list(path, as_list=False,
427+
**kwargs)
428+
manager = GroupIssueManager(self.manager.gitlab,
429+
parent=self.manager._parent)
430+
# FIXME(gpocentek): the computed manager path is not correct
431+
return RESTObjectList(manager, GroupIssue, data_list)
432+
433+
@cli.register_custom_action('GroupMilestone')
434+
@exc.on_http_error(exc.GitlabListError)
435+
def merge_requests(self, **kwargs):
436+
"""List the merge requests related to this milestone.
437+
438+
Args:
439+
**kwargs: Extra options to send to the server (e.g. sudo)
440+
441+
Raises:
442+
GitlabAuthenticationError: If authentication is not correct
443+
GitlabListError: If the list could not be retrieved
444+
445+
Returns:
446+
RESTObjectList: The list of merge requests
447+
"""
448+
path = '%s/%s/merge_requests' % (self.manager.path, self.get_id())
449+
data_list = self.manager.gitlab.http_list(path, as_list=False,
450+
**kwargs)
451+
manager = GroupIssueManager(self.manager.gitlab,
452+
parent=self.manager._parent)
453+
# FIXME(gpocentek): the computed manager path is not correct
454+
return RESTObjectList(manager, GroupMergeRequest, data_list)
455+
456+
457+
class GroupMilestoneManager(CRUDMixin, RESTManager):
458+
_path = '/groups/%(group_id)s/milestones'
459+
_obj_cls = GroupMilestone
460+
_from_parent_attrs = {'group_id': 'id'}
461+
_create_attrs = (('title', ), ('description', 'due_date', 'start_date'))
462+
_update_attrs = (tuple(), ('title', 'description', 'due_date',
463+
'start_date', 'state_event'))
464+
_list_filters = ('iids', 'state', 'search')
465+
466+
398467
class GroupNotificationSettings(NotificationSettings):
399468
pass
400469

@@ -434,6 +503,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
434503
_managers = (
435504
('accessrequests', 'GroupAccessRequestManager'),
436505
('members', 'GroupMemberManager'),
506+
('milestones', 'GroupMilestoneManager'),
437507
('notificationsettings', 'GroupNotificationSettingsManager'),
438508
('projects', 'GroupProjectManager'),
439509
('issues', 'GroupIssueManager'),
@@ -1293,8 +1363,8 @@ def issues(self, **kwargs):
12931363
path = '%s/%s/issues' % (self.manager.path, self.get_id())
12941364
data_list = self.manager.gitlab.http_list(path, as_list=False,
12951365
**kwargs)
1296-
manager = ProjectCommitManager(self.manager.gitlab,
1297-
parent=self.manager._parent)
1366+
manager = ProjectIssueManager(self.manager.gitlab,
1367+
parent=self.manager._parent)
12981368
# FIXME(gpocentek): the computed manager path is not correct
12991369
return RESTObjectList(manager, ProjectIssue, data_list)
13001370

@@ -1316,8 +1386,8 @@ def merge_requests(self, **kwargs):
13161386
path = '%s/%s/merge_requests' % (self.manager.path, self.get_id())
13171387
data_list = self.manager.gitlab.http_list(path, as_list=False,
13181388
**kwargs)
1319-
manager = ProjectCommitManager(self.manager.gitlab,
1320-
parent=self.manager._parent)
1389+
manager = ProjectMergeRequestManager(self.manager.gitlab,
1390+
parent=self.manager._parent)
13211391
# FIXME(gpocentek): the computed manager path is not correct
13221392
return RESTObjectList(manager, ProjectMergeRequest, data_list)
13231393

@@ -1330,7 +1400,7 @@ class ProjectMilestoneManager(CRUDMixin, RESTManager):
13301400
'state_event'))
13311401
_update_attrs = (tuple(), ('title', 'description', 'due_date',
13321402
'start_date', 'state_event'))
1333-
_list_filters = ('iids', 'state')
1403+
_list_filters = ('iids', 'state', 'search')
13341404

13351405

13361406
class ProjectLabel(SubscribableMixin, SaveMixin, ObjectDeleteMixin,

tools/python_test_v4.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,18 @@
170170
settings = group2.notificationsettings.get()
171171
assert(settings.level == 'disabled')
172172

173+
# group milestones
174+
gm1 = group1.milestones.create({'title': 'groupmilestone1'})
175+
assert(len(group1.milestones.list()) == 1)
176+
gm1.due_date = '2020-01-01T00:00:00Z'
177+
gm1.save()
178+
gm1.state_event = 'close'
179+
gm1.save()
180+
gm1 = group1.milestones.get(gm1.id)
181+
assert(gm1.state == 'closed')
182+
assert(len(gm1.issues()) == 0)
183+
assert(len(gm1.merge_requests()) == 0)
184+
173185
# group variables
174186
group1.variables.create({'key': 'foo', 'value': 'bar'})
175187
g_v = group1.variables.get('foo')
@@ -330,8 +342,10 @@
330342
m1.save()
331343
m1.state_event = 'close'
332344
m1.save()
333-
m1 = admin_project.milestones.get(1)
345+
m1 = admin_project.milestones.get(m1.id)
334346
assert(m1.state == 'closed')
347+
assert(len(m1.issues()) == 0)
348+
assert(len(m1.merge_requests()) == 0)
335349

336350
# issues
337351
issue1 = admin_project.issues.create({'title': 'my issue 1',

0 commit comments

Comments
 (0)