1from gitlab import exceptions as exc 2from gitlab import types 3from gitlab.base import RequiredOptional, RESTManager, RESTObject 4from gitlab.mixins import ( 5 CreateMixin, 6 CRUDMixin, 7 DeleteMixin, 8 ListMixin, 9 ObjectDeleteMixin, 10 SaveMixin, 11 UpdateMixin, 12) 13 14from .events import GroupEpicResourceLabelEventManager # noqa: F401 15 16__all__ = [ 17 "GroupEpic", 18 "GroupEpicManager", 19 "GroupEpicIssue", 20 "GroupEpicIssueManager", 21] 22 23 24class GroupEpic(ObjectDeleteMixin, SaveMixin, RESTObject): 25 _id_attr = "iid" 26 _managers = ( 27 ("issues", "GroupEpicIssueManager"), 28 ("resourcelabelevents", "GroupEpicResourceLabelEventManager"), 29 ) 30 31 32class GroupEpicManager(CRUDMixin, RESTManager): 33 _path = "/groups/%(group_id)s/epics" 34 _obj_cls = GroupEpic 35 _from_parent_attrs = {"group_id": "id"} 36 _list_filters = ("author_id", "labels", "order_by", "sort", "search") 37 _create_attrs = RequiredOptional( 38 required=("title",), 39 optional=("labels", "description", "start_date", "end_date"), 40 ) 41 _update_attrs = RequiredOptional( 42 optional=("title", "labels", "description", "start_date", "end_date"), 43 ) 44 _types = {"labels": types.ListAttribute} 45 46 47class GroupEpicIssue(ObjectDeleteMixin, SaveMixin, RESTObject): 48 _id_attr = "epic_issue_id" 49 50 def save(self, **kwargs): 51 """Save the changes made to the object to the server. 52 53 The object is updated to match what the server returns. 54 55 Args: 56 **kwargs: Extra options to send to the server (e.g. sudo) 57 58 Raise: 59 GitlabAuthenticationError: If authentication is not correct 60 GitlabUpdateError: If the server cannot perform the request 61 """ 62 updated_data = self._get_updated_data() 63 # Nothing to update. Server fails if sent an empty dict. 64 if not updated_data: 65 return 66 67 # call the manager 68 obj_id = self.get_id() 69 self.manager.update(obj_id, updated_data, **kwargs) 70 71 72class GroupEpicIssueManager( 73 ListMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager 74): 75 _path = "/groups/%(group_id)s/epics/%(epic_iid)s/issues" 76 _obj_cls = GroupEpicIssue 77 _from_parent_attrs = {"group_id": "group_id", "epic_iid": "iid"} 78 _create_attrs = RequiredOptional(required=("issue_id",)) 79 _update_attrs = RequiredOptional(optional=("move_before_id", "move_after_id")) 80 81 @exc.on_http_error(exc.GitlabCreateError) 82 def create(self, data, **kwargs): 83 """Create a new object. 84 85 Args: 86 data (dict): Parameters to send to the server to create the 87 resource 88 **kwargs: Extra options to send to the server (e.g. sudo) 89 90 Raises: 91 GitlabAuthenticationError: If authentication is not correct 92 GitlabCreateError: If the server cannot perform the request 93 94 Returns: 95 RESTObject: A new instance of the manage object class build with 96 the data sent by the server 97 """ 98 CreateMixin._check_missing_create_attrs(self, data) 99 path = "%s/%s" % (self.path, data.pop("issue_id")) 100 server_data = self.gitlab.http_post(path, **kwargs) 101 # The epic_issue_id attribute doesn't exist when creating the resource, 102 # but is used everywhere elese. Let's create it to be consistent client 103 # side 104 server_data["epic_issue_id"] = server_data["id"] 105 return self._obj_cls(self, server_data) 106