Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit29879d6

Browse files
d0c-s4vageGauvain Pocentek
authored and
Gauvain Pocentek
committed
adds project upload feature (#239)
1 parentfd40fce commit29879d6

File tree

13 files changed

+268
-8
lines changed

13 files changed

+268
-8
lines changed

‎docs/gl_objects/projects.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,3 +368,29 @@
368368
# board lists delete
369369
b_list.delete()
370370
# end board lists delete
371+
372+
# project file upload by path
373+
# Or provide a full path to the uploaded file
374+
project.upload("filename.txt",filepath="/some/path/filename.txt")
375+
# end project file upload by path
376+
377+
# project file upload with data
378+
# Upload a file using its filename and filedata
379+
project.upload("filename.txt",filedata="Raw data")
380+
# end project file upload with data
381+
382+
# project file upload markdown
383+
uploaded_file=project.upload_file("filename.txt",filedata="data")
384+
issue=project.issues.get(issue_id)
385+
issue.notes.create({
386+
"body":"See the attached file: {}".format(uploaded_file["markdown"])
387+
})
388+
# project file upload markdown
389+
390+
# project file upload markdown custom
391+
uploaded_file=project.upload_file("filename.txt",filedata="data")
392+
issue=project.issues.get(issue_id)
393+
issue.notes.create({
394+
"body":"See the [attached file]({})".format(uploaded_file["url"])
395+
})
396+
# project file upload markdown

‎docs/gl_objects/projects.rst

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,3 +779,51 @@ Delete a list:
779779
..literalinclude::projects.py
780780
:start-after: # board lists delete
781781
:end-before: # end board lists delete
782+
783+
784+
File Uploads
785+
============
786+
787+
Reference
788+
---------
789+
790+
* v4 API:
791+
792+
+:attr:`gitlab.v4.objects.Project.upload`
793+
+:class:`gitlab.v4.objects.ProjectUpload`
794+
795+
* v3 API:
796+
797+
+:attr:`gitlab.v3.objects.Project.upload`
798+
+:class:`gitlab.v3.objects.ProjectUpload`
799+
800+
* Gitlab API: https://docs.gitlab.com/ce/api/projects.html#upload-a-file
801+
802+
Examples
803+
--------
804+
805+
Upload a file into a project using a filesystem path:
806+
807+
..literalinclude::projects.py
808+
:start-after: # project file upload by path
809+
:end-before: # end project file upload by path
810+
811+
Upload a file into a project without a filesystem path:
812+
813+
..literalinclude::projects.py
814+
:start-after: # project file upload with data
815+
:end-before: # end project file upload with data
816+
817+
Upload a file and comment on an issue using the uploaded file's
818+
markdown:
819+
820+
..literalinclude::projects.py
821+
:start-after: # project file upload markdown
822+
:end-before: # end project file upload markdown
823+
824+
Upload a file and comment on an issue while using custom
825+
markdown to reference the uploaded file:
826+
827+
..literalinclude::projects.py
828+
:start-after: # project file upload markdown custom
829+
:end-before: # end project file upload markdown custom

‎gitlab/__init__.py

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

397397
returnresults
398398

399-
def_raw_post(self,path_,data=None,content_type=None,**kwargs):
399+
def_raw_post(self,path_,data=None,content_type=None,
400+
files=None,**kwargs):
400401
url='%s%s'% (self._url,path_)
401402
opts=self._get_session_opts(content_type)
402403
try:
403-
returnself.session.post(url,params=kwargs,data=data,**opts)
404+
returnself.session.post(url,params=kwargs,data=data,
405+
files=files,**opts)
404406
exceptExceptionase:
405407
raiseGitlabConnectionError(
406408
"Can't connect to GitLab server (%s)"%e)
@@ -628,7 +630,7 @@ def _build_url(self, path):
628630
return'%s%s'% (self._url,path)
629631

630632
defhttp_request(self,verb,path,query_data={},post_data={},
631-
streamed=False,**kwargs):
633+
streamed=False,files=None,**kwargs):
632634
"""Make an HTTP request to the Gitlab server.
633635
634636
Args:
@@ -658,6 +660,11 @@ def sanitized_url(url):
658660
params=query_data.copy()
659661
params.update(kwargs)
660662
opts=self._get_session_opts(content_type='application/json')
663+
664+
# don't set the content-type header when uploading files
665+
iffilesisnotNone:
666+
delopts["headers"]["Content-type"]
667+
661668
verify=opts.pop('verify')
662669
timeout=opts.pop('timeout')
663670

@@ -668,7 +675,7 @@ def sanitized_url(url):
668675
# always agree with this decision (this is the case with a default
669676
# gitlab installation)
670677
req=requests.Request(verb,url,json=post_data,params=params,
671-
**opts)
678+
files=files,**opts)
672679
prepped=self.session.prepare_request(req)
673680
prepped.url=sanitized_url(prepped.url)
674681
result=self.session.send(prepped,stream=streamed,verify=verify,
@@ -756,7 +763,8 @@ def http_list(self, path, query_data={}, as_list=None, **kwargs):
756763
# No pagination, generator requested
757764
returnGitlabList(self,url,query_data,**kwargs)
758765

759-
defhttp_post(self,path,query_data={},post_data={},**kwargs):
766+
defhttp_post(self,path,query_data={},post_data={},files=None,
767+
**kwargs):
760768
"""Make a POST request to the Gitlab server.
761769
762770
Args:
@@ -776,7 +784,7 @@ def http_post(self, path, query_data={}, post_data={}, **kwargs):
776784
GitlabParsingError: If the json data could not be parsed
777785
"""
778786
result=self.http_request('post',path,query_data=query_data,
779-
post_data=post_data,**kwargs)
787+
post_data=post_data,files=files,**kwargs)
780788
try:
781789
ifresult.headers.get('Content-Type',None)=='application/json':
782790
returnresult.json()

‎gitlab/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ def __ne__(self, other):
536536
classRESTObject(object):
537537
"""Represents an object built from server data.
538538
539-
It holds the attributes know fromte server, and the updated attributes in
539+
It holds the attributes know fromthe server, and the updated attributes in
540540
another. This allows smart updates, if the object allows it.
541541
542542
You can redefine ``_id_attr`` in child classes to specify which attribute

‎gitlab/exceptions.py

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

175175

176+
classGitlabUploadError(GitlabOperationError):
177+
pass
178+
179+
180+
classGitlabAttachFileError(GitlabOperationError):
181+
pass
182+
183+
176184
classGitlabCherryPickError(GitlabOperationError):
177185
pass
178186

‎gitlab/v3/cli.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@
6868
'unstar': {'required': ['id']},
6969
'archive': {'required': ['id']},
7070
'unarchive': {'required': ['id']},
71-
'share': {'required': ['id','group-id','group-access']}},
71+
'share': {'required': ['id','group-id','group-access']},
72+
'upload': {'required': ['id','filename','filepath']}},
7273
gitlab.v3.objects.User: {
7374
'block': {'required': ['id']},
7475
'unblock': {'required': ['id']},
@@ -348,6 +349,20 @@ def do_user_getbyusername(self, cls, gl, what, args):
348349
exceptExceptionase:
349350
cli.die("Impossible to get user %s"%args['query'],e)
350351

352+
defdo_project_upload(self,cls,gl,what,args):
353+
try:
354+
project=gl.projects.get(args["id"])
355+
exceptExceptionase:
356+
cli.die("Could not load project '{!r}'".format(args["id"]),e)
357+
358+
try:
359+
res=project.upload(filename=args["filename"],
360+
filepath=args["filepath"])
361+
exceptExceptionase:
362+
cli.die("Could not upload file into project",e)
363+
364+
returnres
365+
351366

352367
def_populate_sub_parser_by_class(cls,sub_parser):
353368
foraction_namein ['list','get','create','update','delete']:
@@ -469,6 +484,7 @@ def run(gl, what, action, args, verbose, *fargs, **kwargs):
469484
cli.die("Unknown object: %s"%what)
470485

471486
g_cli=GitlabCLI()
487+
472488
method=None
473489
what=what.replace('-','_')
474490
action=action.lower().replace('-','')
@@ -491,6 +507,9 @@ def run(gl, what, action, args, verbose, *fargs, **kwargs):
491507
print("")
492508
else:
493509
print(o)
510+
elifisinstance(ret_val,dict):
511+
fork,vinsix.iteritems(ret_val):
512+
print("{} = {}".format(k,v))
494513
elifisinstance(ret_val,gitlab.base.GitlabObject):
495514
ret_val.display(verbose)
496515
elifisinstance(ret_val,six.string_types):

‎gitlab/v3/objects.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,10 @@ class ProjectIssueNote(GitlabObject):
909909
requiredCreateAttrs= ['body']
910910
optionalCreateAttrs= ['created_at']
911911

912+
# file attachment settings (see #56)
913+
description_attr="body"
914+
project_id_attr="project_id"
915+
912916

913917
classProjectIssueNoteManager(BaseManager):
914918
obj_cls=ProjectIssueNote
@@ -933,6 +937,10 @@ class ProjectIssue(GitlabObject):
933937
[('project_id','project_id'), ('issue_id','id')]),
934938
)
935939

940+
# file attachment settings (see #56)
941+
description_attr="description"
942+
project_id_attr="project_id"
943+
936944
defsubscribe(self,**kwargs):
937945
"""Subscribe to an issue.
938946
@@ -1057,6 +1065,7 @@ class ProjectIssueManager(BaseManager):
10571065

10581066
classProjectMember(GitlabObject):
10591067
_url='/projects/%(project_id)s/members'
1068+
10601069
requiredUrlAttrs= ['project_id']
10611070
requiredCreateAttrs= ['access_level','user_id']
10621071
optionalCreateAttrs= ['expires_at']
@@ -2096,6 +2105,60 @@ def trigger_build(self, ref, token, variables={}, **kwargs):
20962105
r=self.gitlab._raw_post(url,data=data,**kwargs)
20972106
raise_error_from_response(r,GitlabCreateError,201)
20982107

2108+
# see #56 - add file attachment features
2109+
defupload(self,filename,filedata=None,filepath=None,**kwargs):
2110+
"""Upload the specified file into the project.
2111+
2112+
.. note::
2113+
2114+
Either ``filedata`` or ``filepath`` *MUST* be specified.
2115+
2116+
Args:
2117+
filename (str): The name of the file being uploaded
2118+
filedata (bytes): The raw data of the file being uploaded
2119+
filepath (str): The path to a local file to upload (optional)
2120+
2121+
Raises:
2122+
GitlabConnectionError: If the server cannot be reached
2123+
GitlabUploadError: If the file upload fails
2124+
GitlabUploadError: If ``filedata`` and ``filepath`` are not
2125+
specified
2126+
GitlabUploadError: If both ``filedata`` and ``filepath`` are
2127+
specified
2128+
2129+
Returns:
2130+
dict: A ``dict`` with the keys:
2131+
* ``alt`` - The alternate text for the upload
2132+
* ``url`` - The direct url to the uploaded file
2133+
* ``markdown`` - Markdown for the uploaded file
2134+
"""
2135+
iffilepathisNoneandfiledataisNone:
2136+
raiseGitlabUploadError("No file contents or path specified")
2137+
2138+
iffiledataisnotNoneandfilepathisnotNone:
2139+
raiseGitlabUploadError("File contents and file path specified")
2140+
2141+
iffilepathisnotNone:
2142+
withopen(filepath,"rb")asf:
2143+
filedata=f.read()
2144+
2145+
url= ("/projects/%(id)s/uploads"% {
2146+
"id":self.id,
2147+
})
2148+
r=self.gitlab._raw_post(
2149+
url,
2150+
files={"file": (filename,filedata)},
2151+
)
2152+
# returns 201 status code (created)
2153+
raise_error_from_response(r,GitlabUploadError,expected_code=201)
2154+
data=r.json()
2155+
2156+
return {
2157+
"alt":data['alt'],
2158+
"url":data['url'],
2159+
"markdown":data['markdown']
2160+
}
2161+
20992162

21002163
classRunner(GitlabObject):
21012164
_url='/runners'

‎gitlab/v4/cli.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,8 @@ def get_dict(obj):
324324
else:
325325
print(obj)
326326
print('')
327+
elifisinstance(ret_val,dict):
328+
printer.display(ret_val,verbose=verbose,obj=ret_val)
327329
elifisinstance(ret_val,gitlab.base.RESTObject):
328330
printer.display(get_dict(ret_val),verbose=verbose,obj=ret_val)
329331
elifisinstance(ret_val,six.string_types):

‎gitlab/v4/objects.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2071,6 +2071,59 @@ def trigger_pipeline(self, ref, token, variables={}, **kwargs):
20712071
post_data.update(form)
20722072
self.manager.gitlab.http_post(path,post_data=post_data,**kwargs)
20732073

2074+
# see #56 - add file attachment features
2075+
@cli.register_custom_action('Project', ('filename','filepath'))
2076+
@exc.on_http_error(exc.GitlabUploadError)
2077+
defupload(self,filename,filedata=None,filepath=None,**kwargs):
2078+
"""Upload the specified file into the project.
2079+
2080+
.. note::
2081+
2082+
Either ``filedata`` or ``filepath`` *MUST* be specified.
2083+
2084+
Args:
2085+
filename (str): The name of the file being uploaded
2086+
filedata (bytes): The raw data of the file being uploaded
2087+
filepath (str): The path to a local file to upload (optional)
2088+
2089+
Raises:
2090+
GitlabConnectionError: If the server cannot be reached
2091+
GitlabUploadError: If the file upload fails
2092+
GitlabUploadError: If ``filedata`` and ``filepath`` are not
2093+
specified
2094+
GitlabUploadError: If both ``filedata`` and ``filepath`` are
2095+
specified
2096+
2097+
Returns:
2098+
dict: A ``dict`` with the keys:
2099+
* ``alt`` - The alternate text for the upload
2100+
* ``url`` - The direct url to the uploaded file
2101+
* ``markdown`` - Markdown for the uploaded file
2102+
"""
2103+
iffilepathisNoneandfiledataisNone:
2104+
raiseGitlabUploadError("No file contents or path specified")
2105+
2106+
iffiledataisnotNoneandfilepathisnotNone:
2107+
raiseGitlabUploadError("File contents and file path specified")
2108+
2109+
iffilepathisnotNone:
2110+
withopen(filepath,"rb")asf:
2111+
filedata=f.read()
2112+
2113+
url= ('/projects/%(id)s/uploads'% {
2114+
'id':self.id,
2115+
})
2116+
file_info= {
2117+
'file': (filename,filedata),
2118+
}
2119+
data=self.manager.gitlab.http_post(url,files=file_info)
2120+
2121+
return {
2122+
"alt":data['alt'],
2123+
"url":data['url'],
2124+
"markdown":data['markdown']
2125+
}
2126+
20742127

20752128
classRunner(SaveMixin,ObjectDeleteMixin,RESTObject):
20762129
pass

‎tools/cli_test_v3.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ testcase "branch deletion" '
9898
--name branch1 >/dev/null 2>&1
9999
'
100100

101+
testcase"project upload"'
102+
GITLAB project upload --id "$PROJECT_ID" --filename'$(basename$0)' --filepath'$0'
103+
'
104+
101105
testcase"project deletion"'
102106
GITLAB project delete --id "$PROJECT_ID"
103107
'

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp