mercurial: Create mercurial.py module for handling various data requests to hg instance (
bug 1349673) r?mars
- New module with Mercurial class that has methods for accessing JSON data on
hg webserver
MozReview-Commit-ID: KP2tl2SDbiH
new file mode 100644
--- /dev/null
+++ b/commitindex/commitindex/commits/mercurial.py
@@ -0,0 +1,143 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+"""Interface to a Mercurial system."""
+
+from urllib.parse import quote
+import json
+import logging
+import requests
+
+logger = logging.getLogger(__name__)
+
+
+class Mercurial(object):
+ """
+ Interface to a Mercurial system.
+
+ TODO:
+ 1. Load API URL from system wide config
+ """
+
+ def __init__(self, rest_url):
+ self.rest_url = rest_url
+ self.session = requests.Session()
+
+ def call(self, method, path, data=None, raw=False):
+ """Perform API call and decode JSON.
+
+ Generic call function that performs an API call to the
+ Mercurial system and turns the JSON data returned into a
+ Python data object.
+
+ Args:
+ method: Request method such as GET/POST/PUT...
+ path: The resource path of the API call.
+ data: Optional data for the POST method.
+ raw: If True, do not convert from JSON to Python data.
+
+ Returns:
+ A Python object, normally a dict, containing the converted
+ JSON data or the raw text.
+
+ Raises:
+ MercurialError: General error such as invalid JSON or Mercurial
+ returned an error of its own. The code in the latter case will
+ pertain to the specific error code generated by Mercurial.
+ """
+
+ headers = {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json'
+ }
+
+ if method == 'GET':
+ response = self.session.get(
+ self.rest_url + path, params=data, headers=headers
+ )
+
+ if method == 'POST':
+ response = self.session.post(
+ self.rest_url + path, json=data, headers=headers
+ )
+
+ if not raw and \
+ 'Content-Type' in response.headers and \
+ response.headers['Content-Type'] == 'application/json':
+ try:
+ data = json.loads(response.content.decode())
+ except:
+ raise MercurialError("Error decoding JSON data", 400)
+ else:
+ data = str(response.content.decode())
+
+ if isinstance(data, dict) and 'error' in data:
+ raise MercurialError(data['message'], data['code'])
+
+ return data
+
+ def get_commit_data(self, repo, commit):
+ """Get meta data about the commit from Merurial.
+
+ Params:
+ repo: Path for the repo
+ commit: Commit node for the data to retrieve.
+
+ Returns:
+ Python dict containing meta data about commit.
+
+ Raises:
+ MercurialError: General error where the fault code and string will
+ pertain to the specific error code generated by Mercurial.
+ """
+
+ try:
+ commit_data = self.call(
+ 'GET',
+ '/' + quote(str(repo)) + '/json-rev/' + quote(str(commit))
+ )
+ except MercurialError as error:
+ raise MercurialError(
+ 'Resource not found or Mercurial server not available.',
+ error.fault_code
+ )
+
+ return commit_data
+
+ def get_commit_diff(self, repo, commit):
+ """Get raw diff belonging the commit from Merurial.
+
+ Params:
+ repo: Path for the repo
+ commit: Commit node for the data to retrieve.
+
+ Returns:
+ Raw diff (text)
+
+ Raises:
+ MercurialError: General error where the fault code and string will
+ pertain to the specific error code generated by Mercurial.
+ """
+
+ try:
+ diff = self.call(
+ 'GET',
+ '/' + quote(str(repo)) + '/raw-rev/' + quote(str(commit)),
+ raw=True
+ )
+ except MercurialError as error:
+ raise MercurialError(
+ 'Resource not found or Mercurial server not available.',
+ error.fault_code
+ )
+
+ return diff
+
+
+class MercurialError(Exception):
+ """Generic Merurial Exception"""
+
+ def __init__(self, msg, code=None):
+ super(MercurialError, self).__init__(msg)
+ self.fault_code = int(code)
+ self.fault_string = str(msg)