diff options
| author | Gauvain Pocentek <gauvain@pocentek.net> | 2018-05-21 10:30:46 +0200 |
|---|---|---|
| committer | Gauvain Pocentek <gauvain@pocentek.net> | 2018-05-21 10:30:46 +0200 |
| commit | 174185bd45abb7c99cf28432a227660023d53632 (patch) | |
| tree | 6aca21979a07d1c0a5e50a767c760240049d43d7 /gitlab | |
| parent | 175abe950c9f08dc9f66de21b20e7f4df5454517 (diff) | |
| download | gitlab-174185bd45abb7c99cf28432a227660023d53632.tar.gz | |
Add support for user avatar upload
Fixes #308
Diffstat (limited to 'gitlab')
| -rw-r--r-- | gitlab/__init__.py | 21 | ||||
| -rw-r--r-- | gitlab/mixins.py | 39 | ||||
| -rw-r--r-- | gitlab/types.py | 10 | ||||
| -rw-r--r-- | gitlab/v4/objects.py | 9 |
4 files changed, 60 insertions, 19 deletions
diff --git a/gitlab/__init__.py b/gitlab/__init__.py index af38680..3a36bf2 100644 --- a/gitlab/__init__.py +++ b/gitlab/__init__.py @@ -356,20 +356,25 @@ class Gitlab(object): opts = self._get_session_opts(content_type='application/json') - # don't set the content-type header when uploading files - if files is not None: - del opts["headers"]["Content-type"] - verify = opts.pop('verify') timeout = opts.pop('timeout') + # We need to deal with json vs. data when uploading files + if files: + data = post_data + json = None + del opts["headers"]["Content-type"] + else: + json = post_data + data = None + # Requests assumes that `.` should not be encoded as %2E and will make # changes to urls using this encoding. Using a prepped request we can # get the desired behavior. # The Requests behavior is right but it seems that web servers don't # always agree with this decision (this is the case with a default # gitlab installation) - req = requests.Request(verb, url, json=post_data, params=params, + req = requests.Request(verb, url, json=json, data=data, params=params, files=files, **opts) prepped = self.session.prepare_request(req) prepped.url = sanitized_url(prepped.url) @@ -506,7 +511,8 @@ class Gitlab(object): error_message="Failed to parse the server message") return result - def http_put(self, path, query_data={}, post_data={}, **kwargs): + def http_put(self, path, query_data={}, post_data={}, files=None, + **kwargs): """Make a PUT request to the Gitlab server. Args: @@ -515,6 +521,7 @@ class Gitlab(object): query_data (dict): Data to send as query parameters post_data (dict): Data to send in the body (will be converted to json) + files (dict): The files to send to the server **kwargs: Extra data to make the query (e.g. sudo, per_page, page) Returns: @@ -525,7 +532,7 @@ class Gitlab(object): GitlabParsingError: If the json data could not be parsed """ result = self.http_request('put', path, query_data=query_data, - post_data=post_data, **kwargs) + post_data=post_data, files=files, **kwargs) try: return result.json() except Exception: diff --git a/gitlab/mixins.py b/gitlab/mixins.py index f940d60..17f1196 100644 --- a/gitlab/mixins.py +++ b/gitlab/mixins.py @@ -18,6 +18,7 @@ import gitlab from gitlab import base from gitlab import cli +from gitlab import types as g_types from gitlab import exceptions as exc @@ -171,21 +172,29 @@ class CreateMixin(object): GitlabCreateError: If the server cannot perform the request """ self._check_missing_create_attrs(data) + files = {} # We get the attributes that need some special transformation types = getattr(self, '_types', {}) - if types: # Duplicate data to avoid messing with what the user sent us data = data.copy() for attr_name, type_cls in types.items(): if attr_name in data.keys(): type_obj = type_cls(data[attr_name]) - data[attr_name] = type_obj.get_for_api() + + # if the type if FileAttribute we need to pass the data as + # file + if issubclass(type_cls, g_types.FileAttribute): + k = type_obj.get_file_name(attr_name) + files[attr_name] = (k, data.pop(attr_name)) + else: + data[attr_name] = type_obj.get_for_api() # Handle specific URL for creation path = kwargs.pop('path', self.path) - server_data = self.gitlab.http_post(path, post_data=data, **kwargs) + server_data = self.gitlab.http_post(path, post_data=data, files=files, + **kwargs) return self._obj_cls(self, server_data) @@ -232,15 +241,27 @@ class UpdateMixin(object): path = '%s/%s' % (self.path, id) self._check_missing_update_attrs(new_data) + files = {} # We get the attributes that need some special transformation types = getattr(self, '_types', {}) - for attr_name, type_cls in types.items(): - if attr_name in new_data.keys(): - type_obj = type_cls(new_data[attr_name]) - new_data[attr_name] = type_obj.get_for_api() - - return self.gitlab.http_put(path, post_data=new_data, **kwargs) + if types: + # Duplicate data to avoid messing with what the user sent us + new_data = new_data.copy() + for attr_name, type_cls in types.items(): + if attr_name in new_data.keys(): + type_obj = type_cls(new_data[attr_name]) + + # if the type if FileAttribute we need to pass the data as + # file + if issubclass(type_cls, g_types.FileAttribute): + k = type_obj.get_file_name(attr_name) + files[attr_name] = (k, new_data.pop(attr_name)) + else: + new_data[attr_name] = type_obj.get_for_api() + + return self.gitlab.http_put(path, post_data=new_data, files=files, + **kwargs) class SetMixin(object): diff --git a/gitlab/types.py b/gitlab/types.py index d361222..b32409f 100644 --- a/gitlab/types.py +++ b/gitlab/types.py @@ -44,3 +44,13 @@ class ListAttribute(GitlabAttribute): class LowercaseStringAttribute(GitlabAttribute): def get_for_api(self): return str(self._value).lower() + + +class FileAttribute(GitlabAttribute): + def get_file_name(self, attr_name=None): + return attr_name + + +class ImageAttribute(FileAttribute): + def get_file_name(self, attr_name=None): + return '%s.png' % attr_name if attr_name else 'image.png' diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py index 14bad5a..2d9a6bf 100644 --- a/gitlab/v4/objects.py +++ b/gitlab/v4/objects.py @@ -307,16 +307,19 @@ class UserManager(CRUDMixin, RESTManager): ('email', 'username', 'name', 'password', 'reset_password', 'skype', 'linkedin', 'twitter', 'projects_limit', 'extern_uid', 'provider', 'bio', 'admin', 'can_create_group', 'website_url', - 'skip_confirmation', 'external', 'organization', 'location') + 'skip_confirmation', 'external', 'organization', 'location', 'avatar') ) _update_attrs = ( ('email', 'username', 'name'), ('password', 'skype', 'linkedin', 'twitter', 'projects_limit', 'extern_uid', 'provider', 'bio', 'admin', 'can_create_group', 'website_url', 'skip_confirmation', 'external', 'organization', - 'location') + 'location', 'avatar') ) - _types = {'confirm': types.LowercaseStringAttribute} + _types = { + 'confirm': types.LowercaseStringAttribute, + 'avatar': types.ImageAttribute, + } class CurrentUserEmail(ObjectDeleteMixin, RESTObject): |
