diff mbox

[v3,7/8] views/xmlrpc: Add 'checks' endpoint

Message ID 1446743603-31517-8-git-send-email-stephen.finucane@intel.com
State Accepted
Headers show

Commit Message

Stephen Finucane Nov. 5, 2015, 5:13 p.m. UTC
From: Stephen Finucane <stephenfinucane@hotmail.com>

This includes adding a serializer plus the endpoint itself.

Signed-off-by: Stephen Finucane <stephen.finucane@intel.com>

--

v2: Version now returns a tuple to allow for sematic versioning
---
 patchwork/views/xmlrpc.py | 180 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 178 insertions(+), 2 deletions(-)
diff mbox

Patch

diff --git a/patchwork/views/xmlrpc.py b/patchwork/views/xmlrpc.py
index 8836aff..6bdfe56 100644
--- a/patchwork/views/xmlrpc.py
+++ b/patchwork/views/xmlrpc.py
@@ -32,7 +32,7 @@  from django.http import (
     HttpResponse, HttpResponseRedirect, HttpResponseServerError)
 from django.views.decorators.csrf import csrf_exempt
 
-from patchwork.models import Patch, Project, Person, State
+from patchwork.models import Patch, Project, Person, State, Check
 from patchwork.views import patch_to_mbox
 
 
@@ -326,6 +326,33 @@  def state_to_dict(obj):
     }
 
 
+def check_to_dict(obj):
+    """Return a trimmed down dictionary representation of a Check
+    object which is OK to send to the client."""
+    return {
+        'id': obj.id,
+        'date': unicode(obj.date).encode('utf-8'),
+        'patch': unicode(obj.patch).encode('utf-8'),
+        'patch_id': obj.patch_id,
+        'user': unicode(obj.user).encode('utf-8'),
+        'user_id': obj.user_id,
+        'state': obj.get_state_display(),
+        'target_url': obj.target_url,
+        'description': obj.description,
+        'context': obj.context,
+    }
+
+def patch_check_to_dict(obj):
+    """Return a combined patch check."""
+    state_names = dict(Check.STATE_CHOICES)
+
+    return {
+        'state': state_names[obj.combined_check_state],
+        'total': len(obj.checks),
+        'checks': [check_to_dict(check) for check in obj.checks]
+    }
+
+
 #######################################################################
 # Public XML-RPC methods
 #######################################################################
@@ -343,7 +370,7 @@  def pw_rpc_version():
     Returns:
         Version of the API.
     """
-    return 1
+    return (1, 1, 0)
 
 
 @xmlrpc_method()
@@ -783,3 +810,152 @@  def state_get(state_id):
         return state_to_dict(state)
     except State.DoesNotExist:
         return {}
+
+
+@xmlrpc_method()
+def check_list(filt=None):
+    """List checks matching all of a given set of filters.
+
+    Filter checks by one or more of the below fields:
+
+     * id
+     * user
+     * project_id
+     * patch_id
+
+    It is also possible to specify the number of patches returned via
+    a ``max_count`` filter.
+
+     * max_count
+
+    With the exception of ``max_count``, the specified field of the
+    patches are compared to the search string using a provided
+    field lookup type, which can be one of:
+
+     * iexact
+     * contains
+     * icontains
+     * gt
+     * gte
+     * lt
+     * in
+     * startswith
+     * istartswith
+     * endswith
+     * iendswith
+     * range
+     * year
+     * month
+     * day
+     * isnull
+
+    Please refer to the Django documentation for more information on
+    these field lookup types.
+
+    An example filter would look like so:
+
+    {
+        'user__icontains': 'Joe Bloggs',
+        'max_count': 1,
+    }
+
+    Args:
+        filt (dict): The filters specifying the field to compare, the
+            lookup type and the value to compare against. Keys are of
+            format ``[FIELD_NAME]`` or ``[FIELD_NAME]__[LOOKUP_TYPE]``.
+            Example: ``name__icontains``. Values are plain strings to
+            compare against.
+
+    Returns:
+        A serialized list of Checks matching filters, if any. A list
+        of all Checks if no filter given.
+    """
+    if filt is None:
+        filt = {}
+
+    try:
+        # We allow access to many of the fields. But, some fields are
+        # filtered by raw object so we must lookup by ID instead over
+        # XML-RPC.
+        ok_fields = [
+            'id',
+            'user',
+            'project_id',
+            'patch_id',
+            'max_count',
+        ]
+
+        dfilter = {}
+        max_count = 0
+
+        for key in filt:
+            parts = key.split('__')
+            if parts[0] not in ok_fields:
+                # Invalid field given
+                return []
+            if len(parts) > 1:
+                if LOOKUP_TYPES.count(parts[1]) == 0:
+                    # Invalid lookup type given
+                    return []
+
+            if parts[0] == 'user_id':
+                dfilter['user'] = Person.objects.filter(id=filt[key])[0]
+            if parts[0] == 'project_id':
+                dfilter['patch__project'] = Project.objects.filter(
+                    id=filt[key])[0]
+            elif parts[0] == 'patch_id':
+                dfilter['patch'] = Patch.objects.filter(id=filt[key])[0]
+            elif parts[0] == 'max_count':
+                max_count = filt[key]
+            else:
+                dfilter[key] = filt[key]
+
+        checks = Check.objects.filter(**dfilter)
+
+        if max_count > 0:
+            return map(check_to_dict, checks[:max_count])
+        else:
+            return map(check_to_dict, checks)
+    except Check.DoesNotExist:
+        return []
+
+
+@xmlrpc_method()
+def check_get(check_id):
+    """Get a check by its ID.
+
+    Retrieve a check matching a given check ID, if any exists.
+
+    Args:
+        check_id (int): The ID of the check to retrieve
+
+    Returns:
+        The serialized check matching the ID, if any, else an empty
+        dict.
+    """
+    try:
+        check = Check.objects.filter(id=check_id)[0]
+        return check_to_dict(check)
+    except Check.DoesNotExist:
+        return {}
+
+
+@xmlrpc_method()
+def patch_check_get(patch_id):
+    """Get a patch's combined checks by its ID.
+
+    Retrieve a patch's combined checks for the patch matching a given
+    patch ID, if any exists.
+
+    Args:
+        patch_id (int): The ID of the patch to retrieve checks for
+
+    Returns:
+        The serialized combined patch checks matching the ID, if any,
+        else an empty dict.
+    """
+    try:
+        patch = Patch.objects.filter(id=patch_id)[0]
+        return patch_check_to_dict(patch)
+    except Patch.DoesNotExist:
+        return {}