diff options
Diffstat (limited to 'pygerrit')
| -rw-r--r-- | pygerrit/rest/__init__.py | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/pygerrit/rest/__init__.py b/pygerrit/rest/__init__.py new file mode 100644 index 0000000..9cee718 --- /dev/null +++ b/pygerrit/rest/__init__.py @@ -0,0 +1,174 @@ +# The MIT License +# +# Copyright 2013 Sony Mobile Communications. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +""" Interface to the Gerrit REST API. """ + +import json +import requests + +GERRIT_MAGIC_JSON_PREFIX = ")]}\'\n" +GERRIT_AUTH_SUFFIX = "/a" + + +class GerritRestAPIAuthentication(requests.auth.HTTPDigestAuth): + + """ HTTP Digest Auth with netrc credentials. """ + + def __init__(self, url, username=None, password=None): + self.username = username + self.password = password + if not self.has_credentials(): + (self.username, self.password) = \ + requests.utils.get_netrc_auth(url) + if self.has_credentials(): + super(GerritRestAPIAuthentication, self).__init__(self.username, + self.password) + + def __call__(self, req): + if (self.username and self.password): + req = super(GerritRestAPIAuthentication, self).__call__(req) + return req + + def has_credentials(self): + """ Return True if authentication credentials are present. """ + return (self.username is not None and self.password is not None) + + +class GerritRestAPIError(Exception): + + """ Raised when an error occurs during Gerrit REST API access. """ + + def __init__(self, code, message=None): + super(GerritRestAPIError, self).__init__() + self.code = code + self.message = message + + def __str__(self): + if self.message: + return "%d: %s" % (self.code, self.message) + else: + return "%d" % self.code + + +def _decode_response(response): + """ Decode the `response` received from a REST API call. + + Strip off Gerrit's magic prefix if it is there, and return decoded + JSON content or raw text if it cannot be decoded as JSON. + + Raise GerritRestAPIError if the response contains an HTTP error status + code. + + """ + try: + response.raise_for_status() + except requests.exceptions.HTTPError as e: + raise GerritRestAPIError(response.status_code, str(e)) + + content = response.content + if content.startswith(GERRIT_MAGIC_JSON_PREFIX): + content = content[len(GERRIT_MAGIC_JSON_PREFIX):] + try: + return json.loads(content) + except ValueError: + return content.strip() + + +class GerritRestAPI(object): + + """ Interface to the Gerrit REST API. """ + + def __init__(self, url, username=None, password=None): + """ Constructor. + + `url` is assumed to be the full URL to the server, including the + 'http(s)://' prefix. + + HTTP digest authentication is used with the given `username` and + `password`. If both are not given, an attempt is made to get them + from the netrc file. If that fails, anonymous access is used and + functionality is limited. + + """ + self.kwargs = {} + self.url = url + self.session = requests.session() + + self.url = url.rstrip('/') + auth = GerritRestAPIAuthentication(url, username, password) + if auth.has_credentials(): + self.kwargs['auth'] = auth + if not self.url.endswith(GERRIT_AUTH_SUFFIX): + self.url += GERRIT_AUTH_SUFFIX + else: + if self.url.endswith(GERRIT_AUTH_SUFFIX): + self.url = self.url[: - len(GERRIT_AUTH_SUFFIX)] + + def get(self, endpoint, params=None): + """ Send HTTP GET to `endpoint`. + + Return JSON decoded result. + + """ + kwargs = self.kwargs.copy() + if params: + kwargs['params'] = params + response = self.session.get(self.url + endpoint, **kwargs) + return _decode_response(response) + + def put(self, endpoint, params=None, data=None): + """ Send HTTP PUT to `endpoint`. + + Return JSON decoded result. + + """ + kwargs = self.kwargs.copy() + if params: + kwargs['params'] = params + if data: + kwargs['data'] = data + response = self.session.put(self.url + endpoint, **kwargs) + return _decode_response(response) + + def post(self, endpoint, params=None, data=None): + """ Send HTTP POST to `endpoint`. + + Return JSON decoded result. + + """ + kwargs = self.kwargs.copy() + if params: + kwargs['params'] = params + if data: + kwargs['data'] = data + response = self.session.post(self.url + endpoint, **kwargs) + return _decode_response(response) + + def delete(self, endpoint): + """ Send HTTP DELETE to `endpoint`. + + Return JSON decoded result. + + """ + kwargs = self.kwargs.copy() + response = self.session.delete(self.url + endpoint, **kwargs) + return _decode_response(response) |
