Skip to content

Commit 48b028c

Browse files
committed
added project upload features.
Needs unit tests still. see #56
1 parent 37ee7ea commit 48b028c

File tree

3 files changed

+103
-4
lines changed

3 files changed

+103
-4
lines changed

gitlab/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,11 +340,11 @@ def _raw_list(self, path_, cls, extra_attrs={}, **kwargs):
340340
results.extend(self.list(cls, **args))
341341
return results
342342

343-
def _raw_post(self, path_, data=None, content_type=None, **kwargs):
343+
def _raw_post(self, path_, data=None, content_type=None, files=None, **kwargs):
344344
url = '%s%s' % (self._url, path_)
345345
opts = self._get_session_opts(content_type)
346346
try:
347-
return self.session.post(url, params=kwargs, data=data, **opts)
347+
return self.session.post(url, params=kwargs, data=data, files=files, **opts)
348348
except Exception as e:
349349
raise GitlabConnectionError(
350350
"Can't connect to GitLab server (%s)" % e)

gitlab/exceptions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,14 @@ class GitlabTimeTrackingError(GitlabOperationError):
147147
pass
148148

149149

150+
class GitlabUploadError(GitlabOperationError):
151+
pass
152+
153+
154+
class GitlabAttachFileError(GitlabOperationError):
155+
pass
156+
157+
150158
def raise_error_from_response(response, error, expected_code=200):
151159
"""Tries to parse gitlab error message from response and raises error.
152160

gitlab/objects.py

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,55 @@ def compound_metrics(self, **kwargs):
562562
return self._simple_get('/sidekiq/compound_metrics', **kwargs)
563563

564564

565+
class ProjectUploadable(object):
566+
"""A mixin for objects that allow files to be uploaded/attached.
567+
"""
568+
569+
description_attr = "description"
570+
"""The attribute of the object that newly uploaded files'
571+
markdown should be appended to.
572+
"""
573+
574+
project_id_attr = "project_id"
575+
"""The attribute that specifies the attribute that contains
576+
the project id into which files may be uploaded.
577+
"""
578+
579+
# see #56 - add file uploading/attachment features
580+
def attach_file(self, filename, filedata, **kwargs):
581+
"""Attach a file to the issue
582+
583+
Args:
584+
filename (str): The filename of the file being uploaded.
585+
filedata (str): The raw data of the file being uploaded.
586+
Raises:
587+
GitlabConnectionError: If the server cannot be reached.
588+
"""
589+
project_id = getattr(self, self.project_id_attr, None)
590+
if project_id is None:
591+
raise GitlabAttachFileError("{}'s project id could not be determined (tried using {!r})".format(
592+
self,
593+
self.project_id_attr,
594+
))
595+
596+
project = self.gitlab.projects.get(project_id)
597+
res = project.upload(
598+
filename = filename,
599+
filedata = filedata,
600+
)
601+
602+
orig_desc = getattr(self, self.description_attr, "")
603+
# files are "attached" to issues and comments by uploading the
604+
# the file into the project, and including the returned
605+
# markdown in the description
606+
setattr(self, self.description_attr, orig_desc + "\n\n" + res["markdown"])
607+
608+
# XXX:TODO: Is this correct? any changes to the current object instance
609+
# that have not yet been saved will be saved along with the
610+
# file upload, which *may not* be what the user intended.
611+
self.save()
612+
613+
565614
class UserEmail(GitlabObject):
566615
_url = '/users/%(user_id)s/emails'
567616
canUpdate = False
@@ -1437,20 +1486,24 @@ class ProjectHookManager(BaseManager):
14371486
obj_cls = ProjectHook
14381487

14391488

1440-
class ProjectIssueNote(GitlabObject):
1489+
class ProjectIssueNote(GitlabObject, ProjectUploadable):
14411490
_url = '/projects/%(project_id)s/issues/%(issue_id)s/notes'
14421491
_constructorTypes = {'author': 'User'}
14431492
canDelete = False
14441493
requiredUrlAttrs = ['project_id', 'issue_id']
14451494
requiredCreateAttrs = ['body']
14461495
optionalCreateAttrs = ['created_at']
14471496

1497+
# file attachment settings (see #56)
1498+
description_attr = "body"
1499+
project_id_attr = "project_id"
1500+
14481501

14491502
class ProjectIssueNoteManager(BaseManager):
14501503
obj_cls = ProjectIssueNote
14511504

14521505

1453-
class ProjectIssue(GitlabObject):
1506+
class ProjectIssue(GitlabObject, ProjectUploadable):
14541507
_url = '/projects/%(project_id)s/issues/'
14551508
_constructorTypes = {'author': 'User', 'assignee': 'User',
14561509
'milestone': 'ProjectMilestone'}
@@ -1469,6 +1522,10 @@ class ProjectIssue(GitlabObject):
14691522
[('project_id', 'project_id'), ('issue_id', 'id')]),
14701523
)
14711524

1525+
# file attachment settings (see #56)
1526+
description_attr = "description"
1527+
project_id_attr = "project_id"
1528+
14721529
def subscribe(self, **kwargs):
14731530
"""Subscribe to an issue.
14741531
@@ -1593,6 +1650,7 @@ class ProjectIssueManager(BaseManager):
15931650

15941651
class ProjectMember(GitlabObject):
15951652
_url = '/projects/%(project_id)s/members'
1653+
15961654
requiredUrlAttrs = ['project_id']
15971655
requiredCreateAttrs = ['access_level', 'user_id']
15981656
optionalCreateAttrs = ['expires_at']
@@ -2553,6 +2611,39 @@ def trigger_build(self, ref, token, variables={}, **kwargs):
25532611
r = self.gitlab._raw_post(url, data=data, **kwargs)
25542612
raise_error_from_response(r, GitlabCreateError, 201)
25552613

2614+
# see #56 - add file attachment features
2615+
def upload(self, filename, filedata, **kwargs):
2616+
"""Upload a file into the project. This will return the raw response
2617+
from the gitlab API in the form:
2618+
2619+
{
2620+
"alt": "dk",
2621+
"url": "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png",
2622+
"markdown": "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)"
2623+
}
2624+
2625+
See https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/projects.md#upload-a-file
2626+
for more information.
2627+
2628+
Args:
2629+
filename (str): The name of the file being uploaded
2630+
filedata (bytes): The raw data of the file being uploaded
2631+
2632+
Raises:
2633+
GitlabConnectionError: If the server cannot be reached
2634+
GitlabUploadError: If the file upload fails
2635+
"""
2636+
url = ("/projects/%(id)s/uploads" % {
2637+
"id": self.id,
2638+
})
2639+
r = self.gitlab._raw_post(
2640+
url,
2641+
files = {"file": (filename, filedata)},
2642+
)
2643+
# returns 201 status code (created)
2644+
raise_error_from_response(r, GitlabUploadError, expected_code=201)
2645+
return r.json()
2646+
25562647

25572648
class Runner(GitlabObject):
25582649
_url = '/runners'

0 commit comments

Comments
 (0)