summaryrefslogtreecommitdiff
path: root/gitlab
diff options
context:
space:
mode:
authorGauvain Pocentek <gauvain@pocentek.net>2017-05-27 11:48:36 +0200
committerGauvain Pocentek <gauvain@pocentek.net>2017-06-02 15:41:37 +0200
commitc5ad54062ad767c0d2882f64381ad15c034e8872 (patch)
treeb1ac4570233ced7f0321ba920fc08af2995dcedc /gitlab
parent88900e06761794442716c115229bd1f780cfbcef (diff)
downloadgitlab-c5ad54062ad767c0d2882f64381ad15c034e8872.tar.gz
Add lower-level methods for Gitlab()
Multiple goals: * Support making direct queries to the Gitlab server, without objects and managers. * Progressively remove the need to know about managers and objects in the Gitlab class; the Gitlab should only be an HTTP proxy to the gitlab server. * With this the objects gain control on how they should do requests. The complexities of dealing with object specifics will be moved in the object classes where they belong.
Diffstat (limited to 'gitlab')
-rw-r--r--gitlab/__init__.py221
-rw-r--r--gitlab/exceptions.py8
2 files changed, 229 insertions, 0 deletions
diff --git a/gitlab/__init__.py b/gitlab/__init__.py
index 4adc563..7bc9ad3 100644
--- a/gitlab/__init__.py
+++ b/gitlab/__init__.py
@@ -599,3 +599,224 @@ class Gitlab(object):
r = self._raw_put(url, data=data, content_type='application/json')
raise_error_from_response(r, GitlabUpdateError)
return r.json()
+
+ def _build_url(self, path):
+ """Returns the full url from path.
+
+ If path is already a url, return it unchanged. If it's a path, append
+ it to the stored url.
+
+ This is a low-level method, different from _construct_url _build_url
+ have no knowledge of GitlabObject's.
+
+ Returns:
+ str: The full URL
+ """
+ if path.startswith('http://') or path.startswith('https://'):
+ return path
+ else:
+ return '%s%s' % (self._url, path)
+
+ def http_request(self, verb, path, query_data={}, post_data={},
+ streamed=False, **kwargs):
+ """Make an HTTP request to the Gitlab server.
+
+ Args:
+ verb (str): The HTTP method to call ('get', 'post', 'put',
+ 'delete')
+ path (str): Path or full URL to query ('/projects' or
+ 'http://whatever/v4/api/projecs')
+ query_data (dict): Data to send as query parameters
+ post_data (dict): Data to send in the body (will be converted to
+ json)
+ streamed (bool): Whether the data should be streamed
+ **kwargs: Extra data to make the query (e.g. sudo, per_page, page)
+
+ Returns:
+ A requests result object.
+
+ Raises:
+ GitlabHttpError: When the return code is not 2xx
+ """
+ url = self._build_url(path)
+ params = query_data.copy()
+ params.update(kwargs)
+ opts = self._get_session_opts(content_type='application/json')
+ result = self.session.request(verb, url, json=post_data,
+ params=params, stream=streamed, **opts)
+ if not (200 <= result.status_code < 300):
+ raise GitlabHttpError(response_code=result.status_code)
+ return result
+
+ def http_get(self, path, query_data={}, streamed=False, **kwargs):
+ """Make a GET request to the Gitlab server.
+
+ Args:
+ path (str): Path or full URL to query ('/projects' or
+ 'http://whatever/v4/api/projecs')
+ query_data (dict): Data to send as query parameters
+ streamed (bool): Whether the data should be streamed
+ **kwargs: Extra data to make the query (e.g. sudo, per_page, page)
+
+ Returns:
+ A requests result object is streamed is True or the content type is
+ not json.
+ The parsed json data otherwise.
+
+ Raises:
+ GitlabHttpError: When the return code is not 2xx
+ GitlabParsingError: IF the json data could not be parsed
+ """
+ result = self.http_request('get', path, query_data=query_data,
+ streamed=streamed, **kwargs)
+ if (result.headers['Content-Type'] == 'application/json' and
+ not streamed):
+ try:
+ return result.json()
+ except Exception as e:
+ raise GitlaParsingError(
+ message="Failed to parse the server message")
+ else:
+ return r
+
+ def http_list(self, path, query_data={}, **kwargs):
+ """Make a GET request to the Gitlab server for list-oriented queries.
+
+ Args:
+ path (str): Path or full URL to query ('/projects' or
+ 'http://whatever/v4/api/projecs')
+ query_data (dict): Data to send as query parameters
+ **kwargs: Extra data to make the query (e.g. sudo, per_page, page,
+ all)
+
+ Returns:
+ GitlabList: A generator giving access to the objects. If an ``all``
+ kwarg is defined and True, returns a list of all the objects (will
+ possibly make numerous calls to the Gtilab server and eat a lot of
+ memory)
+
+ Raises:
+ GitlabHttpError: When the return code is not 2xx
+ GitlabParsingError: IF the json data could not be parsed
+ """
+ url = self._build_url(path)
+ get_all = kwargs.pop('all', False)
+ obj_gen = GitlabList(self, url, query_data, **kwargs)
+ return list(obj_gen) if get_all else obj_gen
+
+ def http_post(self, path, query_data={}, post_data={}, **kwargs):
+ """Make a POST request to the Gitlab server.
+
+ Args:
+ path (str): Path or full URL to query ('/projects' or
+ 'http://whatever/v4/api/projecs')
+ query_data (dict): Data to send as query parameters
+ post_data (dict): Data to send in the body (will be converted to
+ json)
+ **kwargs: Extra data to make the query (e.g. sudo, per_page, page)
+
+ Returns:
+ The parsed json returned by the server.
+
+ Raises:
+ GitlabHttpError: When the return code is not 2xx
+ GitlabParsingError: IF the json data could not be parsed
+ """
+ result = self.http_request('post', path, query_data=query_data,
+ post_data=post_data, **kwargs)
+ try:
+ return result.json()
+ except Exception as e:
+ raise GitlabParsingError(message="Failed to parse the server message")
+
+ def http_put(self, path, query_data={}, post_data={}, **kwargs):
+ """Make a PUT request to the Gitlab server.
+
+ Args:
+ path (str): Path or full URL to query ('/projects' or
+ 'http://whatever/v4/api/projecs')
+ query_data (dict): Data to send as query parameters
+ post_data (dict): Data to send in the body (will be converted to
+ json)
+ **kwargs: Extra data to make the query (e.g. sudo, per_page, page)
+
+ Returns:
+ The parsed json returned by the server.
+
+ Raises:
+ GitlabHttpError: When the return code is not 2xx
+ GitlabParsingError: IF the json data could not be parsed
+ """
+ result = self.hhtp_request('put', path, query_data=query_data,
+ post_data=post_data, **kwargs)
+ try:
+ return result.json()
+ except Exception as e:
+ raise GitlabParsingError(message="Failed to parse the server message")
+
+ def http_delete(self, path, **kwargs):
+ """Make a PUT request to the Gitlab server.
+
+ Args:
+ path (str): Path or full URL to query ('/projects' or
+ 'http://whatever/v4/api/projecs')
+ **kwargs: Extra data to make the query (e.g. sudo, per_page, page)
+
+ Returns:
+ True.
+
+ Raises:
+ GitlabHttpError: When the return code is not 2xx
+ """
+ result = self.http_request('delete', path, **kwargs)
+ return True
+
+
+class GitlabList(object):
+ """Generator representing a list of remote objects.
+
+ The object handles the links returned by a query to the API, and will call
+ the API again when needed.
+ """
+
+ def __init__(self, gl, url, query_data, **kwargs):
+ self._gl = gl
+ self._query(url, query_data, **kwargs)
+
+ def _query(self, url, query_data={}, **kwargs):
+ result = self._gl.http_request('get', url, query_data=query_data,
+ **kwargs)
+ try:
+ self._next_url = result.links['next']['url']
+ except KeyError:
+ self._next_url = None
+ self._current_page = result.headers.get('X-Page')
+ self._next_page = result.headers.get('X-Next-Page')
+ self._per_page = result.headers.get('X-Per-Page')
+ self._total_pages = result.headers.get('X-Total-Pages')
+ self._total = result.headers.get('X-Total')
+
+ try:
+ self._data = result.json()
+ except Exception as e:
+ raise GitlabParsingError(message="Failed to parse the server message")
+
+ self._current = 0
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ return self.next()
+
+ def next(self):
+ try:
+ item = self._data[self._current]
+ self._current += 1
+ return item
+ except IndexError:
+ if self._next_url:
+ self._query(self._next_url)
+ return self._data[self._current]
+
+ raise StopIteration
diff --git a/gitlab/exceptions.py b/gitlab/exceptions.py
index c7d1da6..401e44c 100644
--- a/gitlab/exceptions.py
+++ b/gitlab/exceptions.py
@@ -47,6 +47,14 @@ class GitlabOperationError(GitlabError):
pass
+class GitlabHttpError(GitlabError):
+ pass
+
+
+class GitlaParsingError(GitlabHttpError):
+ pass
+
+
class GitlabListError(GitlabOperationError):
pass