@@ -66,6 +66,55 @@ def compound_metrics(self, **kwargs):
6666 return self ._simple_get ('/sidekiq/compound_metrics' , ** kwargs )
6767
6868
69+ class ProjectUploadable (object ):
70+ """A mixin for objects that allow files to be uploaded/attached.
71+ """
72+
73+ description_attr = "description"
74+ """The attribute of the object that newly uploaded files'
75+ markdown should be appended to.
76+ """
77+
78+ project_id_attr = "project_id"
79+ """The attribute that specifies the attribute that contains
80+ the project id into which files may be uploaded.
81+ """
82+
83+ # see #56 - add file uploading/attachment features
84+ def attach_file (self , filename , filedata , ** kwargs ):
85+ """Attach a file to the issue
86+
87+ Args:
88+ filename (str): The filename of the file being uploaded.
89+ filedata (str): The raw data of the file being uploaded.
90+ Raises:
91+ GitlabConnectionError: If the server cannot be reached.
92+ """
93+ project_id = getattr (self , self .project_id_attr , None )
94+ if project_id is None :
95+ raise GitlabAttachFileError ("{}'s project id could not be determined (tried using {!r})" .format (
96+ self ,
97+ self .project_id_attr ,
98+ ))
99+
100+ project = self .gitlab .projects .get (project_id )
101+ res = project .upload (
102+ filename = filename ,
103+ filedata = filedata ,
104+ )
105+
106+ orig_desc = getattr (self , self .description_attr , "" )
107+ # files are "attached" to issues and comments by uploading the
108+ # the file into the project, and including the returned
109+ # markdown in the description
110+ setattr (self , self .description_attr , orig_desc + "\n \n " + res ["markdown" ])
111+
112+ # XXX:TODO: Is this correct? any changes to the current object instance
113+ # that have not yet been saved will be saved along with the
114+ # file upload, which *may not* be what the user intended.
115+ self .save ()
116+
117+
69118class UserEmail (GitlabObject ):
70119 _url = '/users/%(user_id)s/emails'
71120 canUpdate = False
@@ -901,20 +950,24 @@ class ProjectHookManager(BaseManager):
901950 obj_cls = ProjectHook
902951
903952
904- class ProjectIssueNote (GitlabObject ):
953+ class ProjectIssueNote (GitlabObject , ProjectUploadable ):
905954 _url = '/projects/%(project_id)s/issues/%(issue_id)s/notes'
906955 _constructorTypes = {'author' : 'User' }
907956 canDelete = False
908957 requiredUrlAttrs = ['project_id' , 'issue_id' ]
909958 requiredCreateAttrs = ['body' ]
910959 optionalCreateAttrs = ['created_at' ]
911960
961+ # file attachment settings (see #56)
962+ description_attr = "body"
963+ project_id_attr = "project_id"
964+
912965
913966class ProjectIssueNoteManager (BaseManager ):
914967 obj_cls = ProjectIssueNote
915968
916969
917- class ProjectIssue (GitlabObject ):
970+ class ProjectIssue (GitlabObject , ProjectUploadable ):
918971 _url = '/projects/%(project_id)s/issues/'
919972 _constructorTypes = {'author' : 'User' , 'assignee' : 'User' ,
920973 'milestone' : 'ProjectMilestone' }
@@ -933,6 +986,10 @@ class ProjectIssue(GitlabObject):
933986 [('project_id' , 'project_id' ), ('issue_id' , 'id' )]),
934987 )
935988
989+ # file attachment settings (see #56)
990+ description_attr = "description"
991+ project_id_attr = "project_id"
992+
936993 def subscribe (self , ** kwargs ):
937994 """Subscribe to an issue.
938995
@@ -1057,6 +1114,7 @@ class ProjectIssueManager(BaseManager):
10571114
10581115class ProjectMember (GitlabObject ):
10591116 _url = '/projects/%(project_id)s/members'
1117+
10601118 requiredUrlAttrs = ['project_id' ]
10611119 requiredCreateAttrs = ['access_level' , 'user_id' ]
10621120 optionalCreateAttrs = ['expires_at' ]
@@ -2096,6 +2154,39 @@ def trigger_build(self, ref, token, variables={}, **kwargs):
20962154 r = self .gitlab ._raw_post (url , data = data , ** kwargs )
20972155 raise_error_from_response (r , GitlabCreateError , 201 )
20982156
2157+ # see #56 - add file attachment features
2158+ def upload (self , filename , filedata , ** kwargs ):
2159+ """Upload a file into the project. This will return the raw response
2160+ from the gitlab API in the form:
2161+
2162+ {
2163+ "alt": "dk",
2164+ "url": "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png",
2165+ "markdown": ""
2166+ }
2167+
2168+ See https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/projects.md#upload-a-file
2169+ for more information.
2170+
2171+ Args:
2172+ filename (str): The name of the file being uploaded
2173+ filedata (bytes): The raw data of the file being uploaded
2174+
2175+ Raises:
2176+ GitlabConnectionError: If the server cannot be reached
2177+ GitlabUploadError: If the file upload fails
2178+ """
2179+ url = ("/projects/%(id)s/uploads" % {
2180+ "id" : self .id ,
2181+ })
2182+ r = self .gitlab ._raw_post (
2183+ url ,
2184+ files = {"file" : (filename , filedata )},
2185+ )
2186+ # returns 201 status code (created)
2187+ raise_error_from_response (r , GitlabUploadError , expected_code = 201 )
2188+ return r .json ()
2189+
20992190
21002191class Runner (GitlabObject ):
21012192 _url = '/runners'
0 commit comments