diff roundup/rest.py @ 5581:30793a435185 REST-rebased

Code convention improved committer: Ralf Schlatterbeck <rsc@runtux.com>
author Chau Nguyen <dangchau1991@yahoo.com>
date Wed, 30 Jan 2019 10:26:35 +0100
parents d5a54b1851aa
children 519b7fd8c8c3
line wrap: on
line diff
--- a/roundup/rest.py	Wed Jan 30 10:26:34 2019 +0100
+++ b/roundup/rest.py	Wed Jan 30 10:26:35 2019 +0100
@@ -17,54 +17,6 @@
 from roundup import xmlrpc
 
 
-def props_from_args(db, cl, args, itemid=None):
-    class_props = cl.properties.keys()
-    props = {}
-    # props = dict.fromkeys(class_props, None)
-
-    for arg in args:
-        key = arg.name
-        value = arg.value
-        if key not in class_props:
-            continue
-        if isinstance(key, unicode):
-            try:
-                key = key.encode('ascii')
-            except UnicodeEncodeError:
-                raise UsageError('argument %r is no valid ascii keyword' % key)
-        if isinstance(value, unicode):
-            value = value.encode('utf-8')
-        if value:
-            try:
-                props[key] = hyperdb.rawToHyperdb(db, cl, itemid, key, value)
-            except hyperdb.HyperdbValueError, msg:
-                raise UsageError(msg)
-        else:
-            props[key] = None
-
-    return props
-
-
-def error_obj(status, msg, source=None):
-    result = {
-        'error': {
-            'status': status,
-            'msg': msg
-        }
-    }
-    if source is not None:
-        result['error']['source'] = source
-
-    return result
-
-
-def data_obj(data):
-    result = {
-        'data': data
-    }
-    return result
-
-
 class RestfulInstance(object):
     """Dummy Handler for REST
     """
@@ -78,33 +30,93 @@
         tracker = self.client.env['TRACKER_NAME']
         self.base_path = '%s://%s/%s/rest/' % (protocol, host, tracker)
 
+    def props_from_args(self, cl, args, itemid=None):
+        class_props = cl.properties.keys()
+        props = {}
+        # props = dict.fromkeys(class_props, None)
+
+        for arg in args:
+            key = arg.name
+            value = arg.value
+            if key not in class_props:
+                continue
+            if isinstance(key, unicode):
+                try:
+                    key = key.encode('ascii')
+                except UnicodeEncodeError:
+                    raise UsageError(
+                        'argument %r is no valid ascii keyword' % key
+                    )
+            if isinstance(value, unicode):
+                value = value.encode('utf-8')
+            if value:
+                try:
+                    props[key] = hyperdb.rawToHyperdb(
+                        self.db, cl, itemid, key, value
+                    )
+                except hyperdb.HyperdbValueError, msg:
+                    raise UsageError(msg)
+            else:
+                props[key] = None
+
+        return props
+
+    @staticmethod
+    def error_obj(status, msg, source=None):
+        result = {
+            'error': {
+                'status': status,
+                'msg': msg
+            }
+        }
+        if source is not None:
+            result['error']['source'] = source
+
+        return result
+
+    @staticmethod
+    def data_obj(data):
+        result = {
+            'data': data
+        }
+        return result
+
     def get_collection(self, class_name, input):
-        if not self.db.security.hasPermission('View', self.db.getuid(),
-                                              class_name):
+        if not self.db.security.hasPermission(
+            'View', self.db.getuid(), class_name
+        ):
             raise Unauthorised('Permission to view %s denied' % class_name)
+
         class_obj = self.db.getclass(class_name)
         class_path = self.base_path + class_name
-        result = [{'id': item_id, 'link': class_path + item_id}
-                  for item_id in class_obj.list()
-                  if self.db.security.hasPermission('View', self.db.getuid(),
-                                                    class_name,
-                                                    itemid=item_id)]
+        result = [
+            {'id': item_id, 'link': class_path + item_id}
+            for item_id in class_obj.list()
+            if self.db.security.hasPermission(
+                'View', self.db.getuid(), class_name, itemid=item_id
+            )
+        ]
         self.client.setHeader("X-Count-Total", str(len(result)))
         return 200, result
 
     def get_element(self, class_name, item_id, input):
-        if not self.db.security.hasPermission('View', self.db.getuid(),
-                                              class_name, itemid=item_id):
-            raise Unauthorised('Permission to view %s item %d denied' %
-                               (class_name, item_id))
+        if not self.db.security.hasPermission(
+            'View', self.db.getuid(), class_name, itemid=item_id
+        ):
+            raise Unauthorised(
+                'Permission to view %s item %d denied' % (class_name, item_id)
+            )
+
         class_obj = self.db.getclass(class_name)
         props = class_obj.properties.keys()
         props.sort()  # sort properties
-        result = [(prop_name, class_obj.get(item_id, prop_name))
-                  for prop_name in props
-                  if self.db.security.hasPermission('View', self.db.getuid(),
-                                                    class_name, prop_name,
-                                                    item_id)]
+        result = [
+            (prop_name, class_obj.get(item_id, prop_name))
+            for prop_name in props
+            if self.db.security.hasPermission(
+                'View', self.db.getuid(), class_name, prop_name,
+            )
+        ]
         result = {
             'id': item_id,
             'type': class_name,
@@ -115,14 +127,15 @@
         return 200, result
 
     def post_collection(self, class_name, input):
-        if not self.db.security.hasPermission('Create', self.db.getuid(),
-                                              class_name):
+        if not self.db.security.hasPermission(
+            'Create', self.db.getuid(), class_name
+        ):
             raise Unauthorised('Permission to create %s denied' % class_name)
 
         class_obj = self.db.getclass(class_name)
 
         # convert types
-        props = props_from_args(self.db, class_obj, input.value)
+        props = self.props_from_args(class_obj, input.value)
 
         # check for the key property
         key = class_obj.getkey()
@@ -130,10 +143,12 @@
             raise UsageError("Must provide the '%s' property." % key)
 
         for key in props:
-            if not self.db.security.hasPermission('Create', self.db.getuid(),
-                                                  class_name, property=key):
-                raise Unauthorised('Permission to create %s.%s denied' %
-                                   (class_name, key))
+            if not self.db.security.hasPermission(
+                'Create', self.db.getuid(), class_name, property=key
+            ):
+                raise Unauthorised(
+                    'Permission to create %s.%s denied' % (class_name, key)
+                )
 
         # do the actual create
         try:
@@ -164,12 +179,15 @@
     def put_element(self, class_name, item_id, input):
         class_obj = self.db.getclass(class_name)
 
-        props = props_from_args(self.db, class_obj, input.value, item_id)
+        props = self.props_from_args(class_obj, input.value, item_id)
         for p in props.iterkeys():
-            if not self.db.security.hasPermission('Edit', self.db.getuid(),
-                                                  class_name, p, item_id):
-                raise Unauthorised('Permission to edit %s of %s%s denied' %
-                                   (p, class_name, item_id))
+            if not self.db.security.hasPermission(
+                'Edit', self.db.getuid(), class_name, p, item_id
+            ):
+                raise Unauthorised(
+                    'Permission to edit %s of %s%s denied' %
+                    (p, class_name, item_id)
+                )
         try:
             result = class_obj.set(item_id, **props)
             self.db.commit()
@@ -185,16 +203,19 @@
         return 200, result
 
     def delete_collection(self, class_name, input):
-        if not self.db.security.hasPermission('Delete', self.db.getuid(),
-                                              class_name):
+        if not self.db.security.hasPermission(
+            'Delete', self.db.getuid(), class_name
+        ):
             raise Unauthorised('Permission to delete %s denied' % class_name)
 
         class_obj = self.db.getclass(class_name)
         for item_id in class_obj.list():
-            if not self.db.security.hasPermission('Delete', self.db.getuid(),
-                                                  class_name, itemid=item_id):
-                raise Unauthorised('Permission to delete %s %s denied' %
-                                   (class_name, item_id))
+            if not self.db.security.hasPermission(
+                'Delete', self.db.getuid(), class_name, itemid=item_id
+            ):
+                raise Unauthorised(
+                    'Permission to delete %s %s denied' % (class_name, item_id)
+                )
 
         count = len(class_obj.list())
         for item_id in class_obj.list():
@@ -209,10 +230,12 @@
         return 200, result
 
     def delete_element(self, class_name, item_id, input):
-        if not self.db.security.hasPermission('Delete', self.db.getuid(),
-                                              class_name, itemid=item_id):
-            raise Unauthorised('Permission to delete %s %s denied' %
-                               (class_name, item_id))
+        if not self.db.security.hasPermission(
+            'Delete', self.db.getuid(), class_name, itemid=item_id
+        ):
+            raise Unauthorised(
+                'Permission to delete %s %s denied' % (class_name, item_id)
+            )
 
         self.db.destroynode(class_name, item_id)
         self.db.commit()
@@ -232,13 +255,17 @@
             op = "replace"
         class_obj = self.db.getclass(class_name)
 
-        props = props_from_args(self.db, class_obj, input.value, item_id)
+        props = self.props_from_args(class_obj, input.value, item_id)
 
         for prop, value in props.iteritems():
-            if not self.db.security.hasPermission('Edit', self.db.getuid(),
-                                                  class_name, prop, item_id):
-                raise Unauthorised('Permission to edit %s of %s%s denied' %
-                                   (prop, class_name, item_id))
+            if not self.db.security.hasPermission(
+                'Edit', self.db.getuid(), class_name, prop, item_id
+            ):
+                raise Unauthorised(
+                    'Permission to edit %s of %s%s denied' %
+                    (prop, class_name, item_id)
+                )
+
             if op == 'add':
                 props[prop] = class_obj.get(item_id, prop) + props[prop]
             elif op == 'replace':
@@ -266,9 +293,11 @@
         return 204, ""
 
     def options_element(self, class_name, item_id, input):
-        self.client.setHeader("Accept-Patch",
-                              "application/x-www-form-urlencoded, "
-                              "multipart/form-data")
+        self.client.setHeader(
+            "Accept-Patch",
+            "application/x-www-form-urlencoded, "
+            "multipart/form-data"
+        )
         return 204, ""
 
     def dispatch(self, method, uri, input):
@@ -279,7 +308,8 @@
         resource_uri = uri.split("/")[1]
 
         # if X-HTTP-Method-Override is set, follow the override method
-        method = self.client.request.headers.getheader('X-HTTP-Method-Override') or method
+        headers = self.client.request.headers
+        method = headers.getheader('X-HTTP-Method-Override') or method
 
         # get the request format for response
         # priority : extension from uri (/rest/issue.json),
@@ -288,7 +318,7 @@
 
         # format_header need a priority parser
         format_ext = os.path.splitext(urlparse.urlparse(uri).path)[1][1:]
-        format_header = self.client.request.headers.getheader('Accept')[12:]
+        format_header = headers.getheader('Accept')[12:]
         format_output = format_ext or format_header or "json"
 
         # check for pretty print
@@ -298,54 +328,65 @@
             pretty_output = False
 
         self.client.setHeader("Access-Control-Allow-Origin", "*")
-        self.client.setHeader("Access-Control-Allow-Headers",
-                              "Content-Type, Authorization, "
-                              "X-HTTP-Method-Override")
+        self.client.setHeader(
+            "Access-Control-Allow-Headers",
+            "Content-Type, Authorization, X-HTTP-Method-Override"
+        )
 
         output = None
         try:
             if resource_uri in self.db.classes:
-                self.client.setHeader("Allow",
-                                      "HEAD, OPTIONS, GET, POST, DELETE")
-                self.client.setHeader("Access-Control-Allow-Methods",
-                                      "HEAD, OPTIONS, GET, POST, DELETE")
-                response_code, output = getattr(self, "%s_collection" % method.lower())(
-                    resource_uri, input)
+                self.client.setHeader(
+                    "Allow",
+                    "HEAD, OPTIONS, GET, POST, DELETE"
+                )
+                self.client.setHeader(
+                    "Access-Control-Allow-Methods",
+                    "HEAD, OPTIONS, GET, POST, DELETE"
+                )
+                response_code, output = getattr(
+                    self, "%s_collection" % method.lower()
+                    )(resource_uri, input)
             else:
                 class_name, item_id = hyperdb.splitDesignator(resource_uri)
-                self.client.setHeader("Allow",
-                                      "HEAD, OPTIONS, GET, PUT, DELETE, PATCH")
-                self.client.setHeader("Access-Control-Allow-Methods",
-                                      "HEAD, OPTIONS, GET, PUT, DELETE, PATCH")
-                response_code, output = getattr(self, "%s_element" % method.lower())(
-                    class_name, item_id, input)
+                self.client.setHeader(
+                    "Allow",
+                    "HEAD, OPTIONS, GET, PUT, DELETE, PATCH"
+                )
+                self.client.setHeader(
+                    "Access-Control-Allow-Methods",
+                    "HEAD, OPTIONS, GET, PUT, DELETE, PATCH"
+                )
+                response_code, output = getattr(
+                    self, "%s_element" % method.lower()
+                    )(class_name, item_id, input)
 
-            output = data_obj(output)
+            output = RestfulInstance.data_obj(output)
             self.client.response_code = response_code
         except IndexError, msg:
-            output = error_obj(404, msg)
+            output = RestfulInstance.error_obj(404, msg)
             self.client.response_code = 404
         except Unauthorised, msg:
-            output = error_obj(403, msg)
+            output = RestfulInstance.error_obj(403, msg)
             self.client.response_code = 403
         except (hyperdb.DesignatorError, UsageError), msg:
-            output = error_obj(400, msg)
+            output = RestfulInstance.error_obj(400, msg)
             self.client.response_code = 400
         except (AttributeError, Reject), msg:
-            output = error_obj(405, msg)
+            output = RestfulInstance.error_obj(405, msg)
             self.client.response_code = 405
         except ValueError, msg:
-            output = error_obj(409, msg)
+            output = RestfulInstance.error_obj(409, msg)
             self.client.response_code = 409
         except NotImplementedError:
-            output = error_obj(402, 'Method is under development')
+            output = RestfulInstance.error_obj(402, 'Method under development')
             self.client.response_code = 402
             # nothing to pay, just a mark for debugging purpose
         except:
             # if self.DEBUG_MODE in roundup_server
             # else msg = 'An error occurred. Please check...',
             exc, val, tb = sys.exc_info()
-            output = error_obj(400, val)
+            output = RestfulInstance.error_obj(400, val)
             self.client.response_code = 400
 
             # out to the logfile, it would be nice if the server do it for me
@@ -361,7 +402,7 @@
                 output = RoundupJSONEncoder(indent=indent).encode(output)
             else:
                 self.client.response_code = 406
-                output = ""
+                output = "Content type is not accepted by client"
 
         return output
 

Roundup Issue Tracker: http://roundup-tracker.org/