Skip to content

Commit 8141f77

Browse files
committed
rebased onto latest in master branch of python-gitlab/python-gitlab
1 parent 0e0d4ae commit 8141f77

File tree

3 files changed

+102
-4
lines changed

3 files changed

+102
-4
lines changed

gitlab/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -396,11 +396,11 @@ def _raw_list(self, path_, cls, **kwargs):
396396

397397
return results
398398

399-
def _raw_post(self, path_, data=None, content_type=None, **kwargs):
399+
def _raw_post(self, path_, data=None, content_type=None, files=None, **kwargs):
400400
url = '%s%s' % (self._url, path_)
401401
opts = self._get_session_opts(content_type)
402402
try:
403-
return self.session.post(url, params=kwargs, data=data, **opts)
403+
return self.session.post(url, params=kwargs, data=data, files=files, **opts)
404404
except Exception as e:
405405
raise GitlabConnectionError(
406406
"Can't connect to GitLab server (%s)" % e)

gitlab/exceptions.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,13 @@ class GitlabTimeTrackingError(GitlabOperationError):
176176
class GitlabCherryPickError(GitlabOperationError):
177177
pass
178178

179+
class GitlabUploadError(GitlabOperationError):
180+
pass
181+
182+
183+
class GitlabAttachFileError(GitlabOperationError):
184+
pass
185+
179186

180187
def raise_error_from_response(response, error, expected_code=200):
181188
"""Tries to parse gitlab error message from response and raises error.

gitlab/v3/objects.py

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
69118
class 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

913966
class 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

10581115
class 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": "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)"
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

21002191
class Runner(GitlabObject):
21012192
_url = '/runners'

0 commit comments

Comments
 (0)