Mercurial > p > roundup > code
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
