diff options
-rw-r--r-- | docs/api-objects.rst | 1 | ||||
-rw-r--r-- | docs/gl_objects/epics.rst | 79 | ||||
-rw-r--r-- | gitlab/v4/objects.py | 78 | ||||
-rwxr-xr-x | tools/ee-test.py | 23 |
4 files changed, 180 insertions, 1 deletions
diff --git a/docs/api-objects.rst b/docs/api-objects.rst index 4e7961d..0cc5014 100644 --- a/docs/api-objects.rst +++ b/docs/api-objects.rst @@ -18,6 +18,7 @@ API examples gl_objects/discussions gl_objects/environments gl_objects/events + gl_objects/epics gl_objects/features gl_objects/geo_nodes gl_objects/groups diff --git a/docs/gl_objects/epics.rst b/docs/gl_objects/epics.rst new file mode 100644 index 0000000..2b1e23e --- /dev/null +++ b/docs/gl_objects/epics.rst @@ -0,0 +1,79 @@ +##### +Epics +##### + +Epics +===== + +Reference +--------- + +* v4 API: + + + :class:`gitlab.v4.objects.GroupEpic` + + :class:`gitlab.v4.objects.GroupEpicManager` + + :attr:`gitlab.Gitlab.Group.epics` + +* GitLab API: https://docs.gitlab.com/ee/api/epics.html (EE feature) + +Examples +-------- + +List the epics for a group:: + + epics = groups.epics.list() + +Get a single epic for a group:: + + epic = group.epics.get(epic_iid) + +Create an epic for a group:: + + epic = group.epics.create({'title': 'My Epic'}) + +Edit an epic:: + + epic.title = 'New title' + epic.labels = ['label1', 'label2'] + epic.save() + +Delete an epic:: + + epic.delete() + +Epics issues +============ + +Reference +--------- + +* v4 API: + + + :class:`gitlab.v4.objects.GroupEpicIssue` + + :class:`gitlab.v4.objects.GroupEpicIssueManager` + + :attr:`gitlab.Gitlab.GroupEpic.issues` + +* GitLab API: https://docs.gitlab.com/ee/api/epic_issues.html (EE feature) + +Examples +-------- + +List the issues associated with an issue:: + + ei = epic.issues.list() + +Associate an issue with an epic:: + + # use the issue id, not its iid + ei = epic.issues.create({'issue_id': 4}) + +Move an issue in the list:: + + ei.move_before_id = epic_issue_id_1 + # or + ei.move_after_id = epic_issue_id_2 + ei.save() + +Delete an issue association:: + + ei.delete() diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py index 7231919..3e16bac 100644 --- a/gitlab/v4/objects.py +++ b/gitlab/v4/objects.py @@ -602,6 +602,83 @@ class GroupCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin, _from_parent_attrs = {'group_id': 'id'} +class GroupEpicIssue(ObjectDeleteMixin, SaveMixin, RESTObject): + _id_attr = 'epic_issue_id' + + def save(self, **kwargs): + """Save the changes made to the object to the server. + + The object is updated to match what the server returns. + + Args: + **kwargs: Extra options to send to the server (e.g. sudo) + + Raise: + GitlabAuthenticationError: If authentication is not correct + GitlabUpdateError: If the server cannot perform the request + """ + updated_data = self._get_updated_data() + # Nothing to update. Server fails if sent an empty dict. + if not updated_data: + return + + # call the manager + obj_id = self.get_id() + self.manager.update(obj_id, updated_data, **kwargs) + + +class GroupEpicIssueManager(ListMixin, CreateMixin, UpdateMixin, DeleteMixin, + RESTManager): + _path = '/groups/%(group_id)s/epics/%(epic_iid)s/issues' + _obj_cls = GroupEpicIssue + _from_parent_attrs = {'group_id': 'group_id', 'epic_iid': 'iid'} + _create_attrs = (('issue_id',), tuple()) + _update_attrs = (tuple(), ('move_before_id', 'move_after_id')) + + @exc.on_http_error(exc.GitlabCreateError) + def create(self, data, **kwargs): + """Create a new object. + + Args: + data (dict): Parameters to send to the server to create the + resource + **kwargs: Extra data to send to the Gitlab server (e.g. sudo) + + Raises: + GitlabAuthenticationError: If authentication is not correct + GitlabCreateError: If the server cannot perform the request + + Returns: + RESTObject: A new instance of the manage object class build with + the data sent by the server + """ + CreateMixin._check_missing_create_attrs(self, data) + path = '%s/%s' % (self.path, data.pop('issue_id')) + server_data = self.gitlab.http_post(path, **kwargs) + # The epic_issue_id attribute doesn't exist when creating the resource, + # but is used everywhere elese. Let's create it to be consistent client + # side + server_data['epic_issue_id'] = server_data['id'] + return self._obj_cls(self, server_data) + + +class GroupEpic(ObjectDeleteMixin, SaveMixin, RESTObject): + _id_attr = 'iid' + _managers = (('issues', 'GroupEpicIssueManager'),) + + +class GroupEpicManager(CRUDMixin, RESTManager): + _path = '/groups/%(group_id)s/epics' + _obj_cls = GroupEpic + _from_parent_attrs = {'group_id': 'id'} + _list_filters = ('author_id', 'labels', 'order_by', 'sort', 'search') + _create_attrs = (('title',), + ('labels', 'description', 'start_date', 'end_date')) + _update_attrs = (tuple(), ('title', 'labels', 'description', 'start_date', + 'end_date')) + _types = {'labels': types.ListAttribute} + + class GroupIssue(RESTObject): pass @@ -762,6 +839,7 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject): ('badges', 'GroupBadgeManager'), ('boards', 'GroupBoardManager'), ('customattributes', 'GroupCustomAttributeManager'), + ('epics', 'GroupEpicManager'), ('issues', 'GroupIssueManager'), ('members', 'GroupMemberManager'), ('milestones', 'GroupMilestoneManager'), diff --git a/tools/ee-test.py b/tools/ee-test.py index b171e68..bc98cc6 100755 --- a/tools/ee-test.py +++ b/tools/ee-test.py @@ -8,6 +8,7 @@ P2 = 'root/project2' MR_P1 = 1 I_P1 = 1 I_P2 = 1 +EPIC_ISSUES = [4, 5] G1 = 'group1' LDAP_CN = 'app1' LDAP_PROVIDER = 'ldapmain' @@ -83,7 +84,7 @@ group1.ldap_sync() group1.delete_ldap_group_link(LDAP_CN) end_log() -start_log('Boards') +start_log('boards') # bit of cleanup just in case for board in project1.boards.list(): if board.name == 'testboard': @@ -121,3 +122,23 @@ try: except Exception as e: assert('The license key is invalid.' in e.error_message) end_log() + +start_log('epics') +epic = group1.epics.create({'title': 'Test epic'}) +epic.title = 'Fixed title' +epic.labels = ['label1', 'label2'] +epic.save() +epic = group1.epics.get(epic.iid) +assert(epic.title == 'Fixed title') +assert(len(group1.epics.list())) + +# issues +assert(not epic.issues.list()) +for i in EPIC_ISSUES: + epic.issues.create({'issue_id': i}) +assert(len(EPIC_ISSUES) == len(epic.issues.list())) +for ei in epic.issues.list(): + ei.delete() + +epic.delete() +end_log() |