Mercurial > p > roundup > code
diff roundup/rest.py @ 5582:519b7fd8c8c3 REST-rebased
Added docstring
committer: Ralf Schlatterbeck <rsc@runtux.com>
| author | Chau Nguyen <dangchau1991@yahoo.com> |
|---|---|
| date | Wed, 30 Jan 2019 10:26:35 +0100 |
| parents | 30793a435185 |
| children | c65d98a16780 |
line wrap: on
line diff
--- a/roundup/rest.py Wed Jan 30 10:26:35 2019 +0100 +++ b/roundup/rest.py Wed Jan 30 10:26:35 2019 +0100 @@ -18,8 +18,7 @@ class RestfulInstance(object): - """Dummy Handler for REST - """ + """The RestfulInstance performs REST request from the client""" def __init__(self, client, db): self.client = client # it might be unnecessary to receive the client @@ -31,6 +30,18 @@ self.base_path = '%s://%s/%s/rest/' % (protocol, host, tracker) def props_from_args(self, cl, args, itemid=None): + """Construct a list of properties from the given arguments, + and return them after validation. + + Args: + cl (string): class object of the resource + args (list): the submitted form of the user + itemid (string, optional): itemid of the object + + Returns: + dict: dictionary of validated properties + + """ class_props = cl.properties.keys() props = {} # props = dict.fromkeys(class_props, None) @@ -63,6 +74,8 @@ @staticmethod def error_obj(status, msg, source=None): + """Wrap the error data into an object. This function is temporally and + will be changed to a decorator later.""" result = { 'error': { 'status': status, @@ -76,12 +89,29 @@ @staticmethod def data_obj(data): + """Wrap the returned data into an object. This function is temporally + and will be changed to a decorator later.""" result = { 'data': data } return result def get_collection(self, class_name, input): + """GET resource from class URI. + + This function returns only items have View permission + class_name should be valid already + + Args: + class_name (string): class name of the resource (Ex: issue, msg) + input (list): the submitted form of the user + + Returns: + int: http status code 200 (OK) + list: list of reference item in the class + id: id of the object + link: path to the object + """ if not self.db.security.hasPermission( 'View', self.db.getuid(), class_name ): @@ -100,6 +130,24 @@ return 200, result def get_element(self, class_name, item_id, input): + """GET resource from object URI. + + This function returns only properties have View permission + class_name and item_id should be valid already + + Args: + class_name (string): class name of the resource (Ex: issue, msg) + item_id (string): id of the resource (Ex: 12, 15) + input (list): the submitted form of the user + + Returns: + int: http status code 200 (OK) + dict: a dictionary represents the object + id: id of the object + type: class name of the object + link: link to the object + attributes: a dictionary represent the attributes of the object + """ if not self.db.security.hasPermission( 'View', self.db.getuid(), class_name, itemid=item_id ): @@ -127,6 +175,21 @@ return 200, result def post_collection(self, class_name, input): + """POST a new object to a class + + If the item is successfully created, the "Location" header will also + contain the link to the created object + + Args: + class_name (string): class name of the resource (Ex: issue, msg) + input (list): the submitted form of the user + + Returns: + int: http status code 201 (Created) + dict: a reference item to the created object + id: id of the object + link: path to the object + """ if not self.db.security.hasPermission( 'Create', self.db.getuid(), class_name ): @@ -171,12 +234,32 @@ return 201, result def post_element(self, class_name, item_id, input): + """POST to an object of a class is not allowed""" raise Reject('POST to an item is not allowed') def put_collection(self, class_name, input): + """PUT a class is not allowed""" raise Reject('PUT a class is not allowed') def put_element(self, class_name, item_id, input): + """PUT a new content to an object + + Replace the content of the existing object + + Args: + class_name (string): class name of the resource (Ex: issue, msg) + item_id (string): id of the resource (Ex: 12, 15) + input (list): the submitted form of the user + + Returns: + int: http status code 200 (OK) + dict: a dictionary represents the modified object + id: id of the object + type: class name of the object + link: link to the object + attributes: a dictionary represent only changed attributes of + the object + """ class_obj = self.db.getclass(class_name) props = self.props_from_args(class_obj, input.value, item_id) @@ -203,6 +286,18 @@ return 200, result def delete_collection(self, class_name, input): + """DELETE all objects in a class + + Args: + class_name (string): class name of the resource (Ex: issue, msg) + input (list): the submitted form of the user + + Returns: + int: http status code 200 (OK) + dict: + status (string): 'ok' + count (int): number of deleted objects + """ if not self.db.security.hasPermission( 'Delete', self.db.getuid(), class_name ): @@ -230,6 +325,18 @@ return 200, result def delete_element(self, class_name, item_id, input): + """DELETE an object in a class + + Args: + class_name (string): class name of the resource (Ex: issue, msg) + item_id (string): id of the resource (Ex: 12, 15) + input (list): the submitted form of the user + + Returns: + int: http status code 200 (OK) + dict: + status (string): 'ok' + """ if not self.db.security.hasPermission( 'Delete', self.db.getuid(), class_name, itemid=item_id ): @@ -246,6 +353,7 @@ return 200, result def patch_collection(self, class_name, input): + """PATCH a class is not allowed""" raise Reject('PATCH a class is not allowed') def patch_element(self, class_name, item_id, input): @@ -290,9 +398,21 @@ return 200, result def options_collection(self, class_name, input): + """OPTION return the HTTP Header for the class uri + + Returns: + int: http status code 204 (No content) + body (string): an empty string + """ return 204, "" def options_element(self, class_name, item_id, input): + """OPTION return the HTTP Header for the object uri + + Returns: + int: http status code 204 (No content) + body (string): an empty string + """ self.client.setHeader( "Accept-Patch", "application/x-www-form-urlencoded, " @@ -301,6 +421,7 @@ return 204, "" def dispatch(self, method, uri, input): + """format and process the request""" # PATH is split to multiple pieces # 0 - rest # 1 - resource @@ -327,40 +448,43 @@ except KeyError: pretty_output = False + # add access-control-allow-* to support CORS self.client.setHeader("Access-Control-Allow-Origin", "*") self.client.setHeader( "Access-Control-Allow-Headers", "Content-Type, Authorization, X-HTTP-Method-Override" ) + 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" + ) + else: + self.client.setHeader( + "Allow", + "HEAD, OPTIONS, GET, PUT, DELETE, PATCH" + ) + self.client.setHeader( + "Access-Control-Allow-Methods", + "HEAD, OPTIONS, GET, PUT, DELETE, PATCH" + ) + # Call the appropriate method 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) 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) - output = RestfulInstance.data_obj(output) self.client.response_code = response_code except IndexError, msg: @@ -408,6 +532,8 @@ class RoundupJSONEncoder(json.JSONEncoder): + """RoundupJSONEncoder overrides the default JSONEncoder to handle all + types of the object without returning any error""" def default(self, obj): try: result = json.JSONEncoder.default(self, obj)
