mercurial: Create mercurial.py module for handling various data requests to hg instance (bug 1349673) r?mars draft
authorDavid Lawrence <dkl@mozilla.com>
Tue, 28 Mar 2017 13:13:06 -0400
changeset 5638 841a6a870a60210f0cc31e06f5606d96834037fc
parent 5637 c6b856365c8aeec6783334ee810e18b891a4535f
child 5639 d58a510ff7859726cb9998c502661b2473572ade
push id228
push userdlawrence@mozilla.com
push dateWed, 05 Apr 2017 21:13:59 +0000
reviewersmars
bugs1349673
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
commitindex/commitindex/commits/mercurial.py
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)