@@ -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+
565614class 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
14491502class 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
15941651class 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": ""
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
25572648class Runner (GitlabObject ):
25582649 _url = '/runners'
0 commit comments