1818
1919
2020class RestfulInstance (object ):
21- """Dummy Handler for REST
22- """
21+ """The RestfulInstance performs REST request from the client"""
2322
2423 def __init__ (self , client , db ):
2524 self .client = client # it might be unnecessary to receive the client
@@ -31,6 +30,18 @@ def __init__(self, client, db):
3130 self .base_path = '%s://%s/%s/rest/' % (protocol , host , tracker )
3231
3332 def props_from_args (self , cl , args , itemid = None ):
33+ """Construct a list of properties from the given arguments,
34+ and return them after validation.
35+
36+ Args:
37+ cl (string): class object of the resource
38+ args (list): the submitted form of the user
39+ itemid (string, optional): itemid of the object
40+
41+ Returns:
42+ dict: dictionary of validated properties
43+
44+ """
3445 class_props = cl .properties .keys ()
3546 props = {}
3647 # props = dict.fromkeys(class_props, None)
@@ -63,6 +74,8 @@ def props_from_args(self, cl, args, itemid=None):
6374
6475 @staticmethod
6576 def error_obj (status , msg , source = None ):
77+ """Wrap the error data into an object. This function is temporally and
78+ will be changed to a decorator later."""
6679 result = {
6780 'error' : {
6881 'status' : status ,
@@ -76,12 +89,29 @@ def error_obj(status, msg, source=None):
7689
7790 @staticmethod
7891 def data_obj (data ):
92+ """Wrap the returned data into an object. This function is temporally
93+ and will be changed to a decorator later."""
7994 result = {
8095 'data' : data
8196 }
8297 return result
8398
8499 def get_collection (self , class_name , input ):
100+ """GET resource from class URI.
101+
102+ This function returns only items have View permission
103+ class_name should be valid already
104+
105+ Args:
106+ class_name (string): class name of the resource (Ex: issue, msg)
107+ input (list): the submitted form of the user
108+
109+ Returns:
110+ int: http status code 200 (OK)
111+ list: list of reference item in the class
112+ id: id of the object
113+ link: path to the object
114+ """
85115 if not self .db .security .hasPermission (
86116 'View' , self .db .getuid (), class_name
87117 ):
@@ -100,6 +130,24 @@ def get_collection(self, class_name, input):
100130 return 200 , result
101131
102132 def get_element (self , class_name , item_id , input ):
133+ """GET resource from object URI.
134+
135+ This function returns only properties have View permission
136+ class_name and item_id should be valid already
137+
138+ Args:
139+ class_name (string): class name of the resource (Ex: issue, msg)
140+ item_id (string): id of the resource (Ex: 12, 15)
141+ input (list): the submitted form of the user
142+
143+ Returns:
144+ int: http status code 200 (OK)
145+ dict: a dictionary represents the object
146+ id: id of the object
147+ type: class name of the object
148+ link: link to the object
149+ attributes: a dictionary represent the attributes of the object
150+ """
103151 if not self .db .security .hasPermission (
104152 'View' , self .db .getuid (), class_name , itemid = item_id
105153 ):
@@ -127,6 +175,21 @@ def get_element(self, class_name, item_id, input):
127175 return 200 , result
128176
129177 def post_collection (self , class_name , input ):
178+ """POST a new object to a class
179+
180+ If the item is successfully created, the "Location" header will also
181+ contain the link to the created object
182+
183+ Args:
184+ class_name (string): class name of the resource (Ex: issue, msg)
185+ input (list): the submitted form of the user
186+
187+ Returns:
188+ int: http status code 201 (Created)
189+ dict: a reference item to the created object
190+ id: id of the object
191+ link: path to the object
192+ """
130193 if not self .db .security .hasPermission (
131194 'Create' , self .db .getuid (), class_name
132195 ):
@@ -171,12 +234,32 @@ def post_collection(self, class_name, input):
171234 return 201 , result
172235
173236 def post_element (self , class_name , item_id , input ):
237+ """POST to an object of a class is not allowed"""
174238 raise Reject ('POST to an item is not allowed' )
175239
176240 def put_collection (self , class_name , input ):
241+ """PUT a class is not allowed"""
177242 raise Reject ('PUT a class is not allowed' )
178243
179244 def put_element (self , class_name , item_id , input ):
245+ """PUT a new content to an object
246+
247+ Replace the content of the existing object
248+
249+ Args:
250+ class_name (string): class name of the resource (Ex: issue, msg)
251+ item_id (string): id of the resource (Ex: 12, 15)
252+ input (list): the submitted form of the user
253+
254+ Returns:
255+ int: http status code 200 (OK)
256+ dict: a dictionary represents the modified object
257+ id: id of the object
258+ type: class name of the object
259+ link: link to the object
260+ attributes: a dictionary represent only changed attributes of
261+ the object
262+ """
180263 class_obj = self .db .getclass (class_name )
181264
182265 props = self .props_from_args (class_obj , input .value , item_id )
@@ -203,6 +286,18 @@ def put_element(self, class_name, item_id, input):
203286 return 200 , result
204287
205288 def delete_collection (self , class_name , input ):
289+ """DELETE all objects in a class
290+
291+ Args:
292+ class_name (string): class name of the resource (Ex: issue, msg)
293+ input (list): the submitted form of the user
294+
295+ Returns:
296+ int: http status code 200 (OK)
297+ dict:
298+ status (string): 'ok'
299+ count (int): number of deleted objects
300+ """
206301 if not self .db .security .hasPermission (
207302 'Delete' , self .db .getuid (), class_name
208303 ):
@@ -230,6 +325,18 @@ def delete_collection(self, class_name, input):
230325 return 200 , result
231326
232327 def delete_element (self , class_name , item_id , input ):
328+ """DELETE an object in a class
329+
330+ Args:
331+ class_name (string): class name of the resource (Ex: issue, msg)
332+ item_id (string): id of the resource (Ex: 12, 15)
333+ input (list): the submitted form of the user
334+
335+ Returns:
336+ int: http status code 200 (OK)
337+ dict:
338+ status (string): 'ok'
339+ """
233340 if not self .db .security .hasPermission (
234341 'Delete' , self .db .getuid (), class_name , itemid = item_id
235342 ):
@@ -246,6 +353,7 @@ def delete_element(self, class_name, item_id, input):
246353 return 200 , result
247354
248355 def patch_collection (self , class_name , input ):
356+ """PATCH a class is not allowed"""
249357 raise Reject ('PATCH a class is not allowed' )
250358
251359 def patch_element (self , class_name , item_id , input ):
@@ -290,9 +398,21 @@ def patch_element(self, class_name, item_id, input):
290398 return 200 , result
291399
292400 def options_collection (self , class_name , input ):
401+ """OPTION return the HTTP Header for the class uri
402+
403+ Returns:
404+ int: http status code 204 (No content)
405+ body (string): an empty string
406+ """
293407 return 204 , ""
294408
295409 def options_element (self , class_name , item_id , input ):
410+ """OPTION return the HTTP Header for the object uri
411+
412+ Returns:
413+ int: http status code 204 (No content)
414+ body (string): an empty string
415+ """
296416 self .client .setHeader (
297417 "Accept-Patch" ,
298418 "application/x-www-form-urlencoded, "
@@ -301,6 +421,7 @@ def options_element(self, class_name, item_id, input):
301421 return 204 , ""
302422
303423 def dispatch (self , method , uri , input ):
424+ """format and process the request"""
304425 # PATH is split to multiple pieces
305426 # 0 - rest
306427 # 1 - resource
@@ -327,40 +448,43 @@ def dispatch(self, method, uri, input):
327448 except KeyError :
328449 pretty_output = False
329450
451+ # add access-control-allow-* to support CORS
330452 self .client .setHeader ("Access-Control-Allow-Origin" , "*" )
331453 self .client .setHeader (
332454 "Access-Control-Allow-Headers" ,
333455 "Content-Type, Authorization, X-HTTP-Method-Override"
334456 )
457+ if resource_uri in self .db .classes :
458+ self .client .setHeader (
459+ "Allow" ,
460+ "HEAD, OPTIONS, GET, POST, DELETE"
461+ )
462+ self .client .setHeader (
463+ "Access-Control-Allow-Methods" ,
464+ "HEAD, OPTIONS, GET, POST, DELETE"
465+ )
466+ else :
467+ self .client .setHeader (
468+ "Allow" ,
469+ "HEAD, OPTIONS, GET, PUT, DELETE, PATCH"
470+ )
471+ self .client .setHeader (
472+ "Access-Control-Allow-Methods" ,
473+ "HEAD, OPTIONS, GET, PUT, DELETE, PATCH"
474+ )
335475
476+ # Call the appropriate method
336477 output = None
337478 try :
338479 if resource_uri in self .db .classes :
339- self .client .setHeader (
340- "Allow" ,
341- "HEAD, OPTIONS, GET, POST, DELETE"
342- )
343- self .client .setHeader (
344- "Access-Control-Allow-Methods" ,
345- "HEAD, OPTIONS, GET, POST, DELETE"
346- )
347480 response_code , output = getattr (
348481 self , "%s_collection" % method .lower ()
349482 )(resource_uri , input )
350483 else :
351484 class_name , item_id = hyperdb .splitDesignator (resource_uri )
352- self .client .setHeader (
353- "Allow" ,
354- "HEAD, OPTIONS, GET, PUT, DELETE, PATCH"
355- )
356- self .client .setHeader (
357- "Access-Control-Allow-Methods" ,
358- "HEAD, OPTIONS, GET, PUT, DELETE, PATCH"
359- )
360485 response_code , output = getattr (
361486 self , "%s_element" % method .lower ()
362487 )(class_name , item_id , input )
363-
364488 output = RestfulInstance .data_obj (output )
365489 self .client .response_code = response_code
366490 except IndexError , msg :
@@ -408,6 +532,8 @@ def dispatch(self, method, uri, input):
408532
409533
410534class RoundupJSONEncoder (json .JSONEncoder ):
535+ """RoundupJSONEncoder overrides the default JSONEncoder to handle all
536+ types of the object without returning any error"""
411537 def default (self , obj ):
412538 try :
413539 result = json .JSONEncoder .default (self , obj )
0 commit comments