comparison roundup/rest.py @ 5587:cb2b320fde16 REST-rebased

Added decorator to handle formatting output data committer: Ralf Schlatterbeck <rsc@runtux.com>
author Chau Nguyen <dangchau1991@yahoo.com>
date Wed, 30 Jan 2019 10:26:35 +0100
parents 53098db851f2
children 6b3a9655a7d9
comparison
equal deleted inserted replaced
5586:8f2fbc88e155 5587:cb2b320fde16
87 except hyperdb.HyperdbValueError, msg: 87 except hyperdb.HyperdbValueError, msg:
88 raise UsageError(msg) 88 raise UsageError(msg)
89 89
90 return prop 90 return prop
91 91
92 @staticmethod 92 def _data_decorator(func):
93 def error_obj(status, msg, source=None): 93 """Wrap the returned data into an object.."""
94 """Wrap the error data into an object. This function is temporally and 94 def format_object(self, *args, **kwargs):
95 will be changed to a decorator later.""" 95 try:
96 result = { 96 code, data = func(self, *args, **kwargs)
97 'error': { 97 except IndexError, msg:
98 'status': status, 98 code = 404
99 'msg': msg 99 data = msg
100 } 100 except Unauthorised, msg:
101 } 101 code = 403
102 if source is not None: 102 data = msg
103 result['error']['source'] = source 103 except (hyperdb.DesignatorError, UsageError), msg:
104 104 code = 400
105 return result 105 data = msg
106 106 except (AttributeError, Reject), msg:
107 @staticmethod 107 code = 405
108 def data_obj(data): 108 data = msg
109 """Wrap the returned data into an object. This function is temporally 109 except ValueError, msg:
110 and will be changed to a decorator later.""" 110 code = 409
111 result = { 111 data = msg
112 'data': data 112 except NotImplementedError:
113 } 113 code = 402 # nothing to pay, just a mark for debugging purpose
114 return result 114 data = 'Method under development'
115 115 except:
116 exc, val, tb = sys.exc_info()
117 code = 400
118 # if self.DEBUG_MODE in roundup_server
119 # else data = 'An error occurred. Please check...',
120 data = val
121
122 # out to the logfile
123 print 'EXCEPTION AT', time.ctime()
124 traceback.print_exc()
125
126 self.client.response_code = code
127 if code >= 400: # any error require error format
128 result = {
129 'error': {
130 'status': code,
131 'msg': data
132 }
133 }
134 else:
135 result = {
136 'data': data
137 }
138 return result
139 return format_object
140
141 @_data_decorator
116 def get_collection(self, class_name, input): 142 def get_collection(self, class_name, input):
117 """GET resource from class URI. 143 """GET resource from class URI.
118 144
119 This function returns only items have View permission 145 This function returns only items have View permission
120 class_name should be valid already 146 class_name should be valid already
144 ) 170 )
145 ] 171 ]
146 self.client.setHeader("X-Count-Total", str(len(result))) 172 self.client.setHeader("X-Count-Total", str(len(result)))
147 return 200, result 173 return 200, result
148 174
175 @_data_decorator
149 def get_element(self, class_name, item_id, input): 176 def get_element(self, class_name, item_id, input):
150 """GET resource from object URI. 177 """GET resource from object URI.
151 178
152 This function returns only properties have View permission 179 This function returns only properties have View permission
153 class_name and item_id should be valid already 180 class_name and item_id should be valid already
189 'attributes': dict(result) 216 'attributes': dict(result)
190 } 217 }
191 218
192 return 200, result 219 return 200, result
193 220
221 @_data_decorator
194 def get_attribute(self, class_name, item_id, attr_name, input): 222 def get_attribute(self, class_name, item_id, attr_name, input):
195 """GET resource from attribute URI. 223 """GET resource from attribute URI.
196 224
197 This function returns only attribute has View permission 225 This function returns only attribute has View permission
198 class_name should be valid already 226 class_name should be valid already
229 'data': data 257 'data': data
230 } 258 }
231 259
232 return 200, result 260 return 200, result
233 261
262 @_data_decorator
234 def post_collection(self, class_name, input): 263 def post_collection(self, class_name, input):
235 """POST a new object to a class 264 """POST a new object to a class
236 265
237 If the item is successfully created, the "Location" header will also 266 If the item is successfully created, the "Location" header will also
238 contain the link to the created object 267 contain the link to the created object
288 'id': item_id, 317 'id': item_id,
289 'link': link 318 'link': link
290 } 319 }
291 return 201, result 320 return 201, result
292 321
322 @_data_decorator
293 def post_element(self, class_name, item_id, input): 323 def post_element(self, class_name, item_id, input):
294 """POST to an object of a class is not allowed""" 324 """POST to an object of a class is not allowed"""
295 raise Reject('POST to an item is not allowed') 325 raise Reject('POST to an item is not allowed')
296 326
327 @_data_decorator
297 def post_attribute(self, class_name, item_id, attr_name, input): 328 def post_attribute(self, class_name, item_id, attr_name, input):
298 """POST to an attribute of an object is not allowed""" 329 """POST to an attribute of an object is not allowed"""
299 raise Reject('POST to an attribute is not allowed') 330 raise Reject('POST to an attribute is not allowed')
300 331
332 @_data_decorator
301 def put_collection(self, class_name, input): 333 def put_collection(self, class_name, input):
302 """PUT a class is not allowed""" 334 """PUT a class is not allowed"""
303 raise Reject('PUT a class is not allowed') 335 raise Reject('PUT a class is not allowed')
304 336
337 @_data_decorator
305 def put_element(self, class_name, item_id, input): 338 def put_element(self, class_name, item_id, input):
306 """PUT a new content to an object 339 """PUT a new content to an object
307 340
308 Replace the content of the existing object 341 Replace the content of the existing object
309 342
344 'link': self.base_path + class_name + item_id, 377 'link': self.base_path + class_name + item_id,
345 'attribute': result 378 'attribute': result
346 } 379 }
347 return 200, result 380 return 200, result
348 381
382 @_data_decorator
349 def put_attribute(self, class_name, item_id, attr_name, input): 383 def put_attribute(self, class_name, item_id, attr_name, input):
350 """PUT an attribute to an object 384 """PUT an attribute to an object
351 385
352 Args: 386 Args:
353 class_name (string): class name of the resource (Ex: issue, msg) 387 class_name (string): class name of the resource (Ex: issue, msg)
392 'attribute': result 426 'attribute': result
393 } 427 }
394 428
395 return 200, result 429 return 200, result
396 430
431 @_data_decorator
397 def delete_collection(self, class_name, input): 432 def delete_collection(self, class_name, input):
398 """DELETE all objects in a class 433 """DELETE all objects in a class
399 434
400 Args: 435 Args:
401 class_name (string): class name of the resource (Ex: issue, msg) 436 class_name (string): class name of the resource (Ex: issue, msg)
431 'count': count 466 'count': count
432 } 467 }
433 468
434 return 200, result 469 return 200, result
435 470
471 @_data_decorator
436 def delete_element(self, class_name, item_id, input): 472 def delete_element(self, class_name, item_id, input):
437 """DELETE an object in a class 473 """DELETE an object in a class
438 474
439 Args: 475 Args:
440 class_name (string): class name of the resource (Ex: issue, msg) 476 class_name (string): class name of the resource (Ex: issue, msg)
459 'status': 'ok' 495 'status': 'ok'
460 } 496 }
461 497
462 return 200, result 498 return 200, result
463 499
500 @_data_decorator
464 def delete_attribute(self, class_name, item_id, attr_name, input): 501 def delete_attribute(self, class_name, item_id, attr_name, input):
465 """DELETE an attribute in a object by setting it to None or empty 502 """DELETE an attribute in a object by setting it to None or empty
466 503
467 Args: 504 Args:
468 class_name (string): class name of the resource (Ex: issue, msg) 505 class_name (string): class name of the resource (Ex: issue, msg)
501 'status': 'ok' 538 'status': 'ok'
502 } 539 }
503 540
504 return 200, result 541 return 200, result
505 542
543 @_data_decorator
506 def patch_collection(self, class_name, input): 544 def patch_collection(self, class_name, input):
507 """PATCH a class is not allowed""" 545 """PATCH a class is not allowed"""
508 raise Reject('PATCH a class is not allowed') 546 raise Reject('PATCH a class is not allowed')
509 547
548 @_data_decorator
510 def patch_element(self, class_name, item_id, input): 549 def patch_element(self, class_name, item_id, input):
511 """PATCH an object 550 """PATCH an object
512 551
513 Patch an element using 3 operators 552 Patch an element using 3 operators
514 ADD : Append new value to the object's attribute 553 ADD : Append new value to the object's attribute
571 'link': self.base_path + class_name + item_id, 610 'link': self.base_path + class_name + item_id,
572 'attribute': result 611 'attribute': result
573 } 612 }
574 return 200, result 613 return 200, result
575 614
615 @_data_decorator
576 def patch_attribute(self, class_name, item_id, attr_name, input): 616 def patch_attribute(self, class_name, item_id, attr_name, input):
577 """PATCH an attribute of an object 617 """PATCH an attribute of an object
578 618
579 Patch an element using 3 operators 619 Patch an element using 3 operators
580 ADD : Append new value to the attribute 620 ADD : Append new value to the attribute
643 'link': self.base_path + class_name + item_id, 683 'link': self.base_path + class_name + item_id,
644 'attribute': result 684 'attribute': result
645 } 685 }
646 return 200, result 686 return 200, result
647 687
688 @_data_decorator
648 def options_collection(self, class_name, input): 689 def options_collection(self, class_name, input):
649 """OPTION return the HTTP Header for the class uri 690 """OPTION return the HTTP Header for the class uri
650 691
651 Returns: 692 Returns:
652 int: http status code 204 (No content) 693 int: http status code 204 (No content)
653 body (string): an empty string 694 body (string): an empty string
654 """ 695 """
655 return 204, "" 696 return 204, ""
656 697
698 @_data_decorator
657 def options_element(self, class_name, item_id, input): 699 def options_element(self, class_name, item_id, input):
658 """OPTION return the HTTP Header for the object uri 700 """OPTION return the HTTP Header for the object uri
659 701
660 Returns: 702 Returns:
661 int: http status code 204 (No content) 703 int: http status code 204 (No content)
665 "Accept-Patch", 707 "Accept-Patch",
666 "application/x-www-form-urlencoded, multipart/form-data" 708 "application/x-www-form-urlencoded, multipart/form-data"
667 ) 709 )
668 return 204, "" 710 return 204, ""
669 711
712 @_data_decorator
670 def option_attribute(self, class_name, item_id, attr_name, input): 713 def option_attribute(self, class_name, item_id, attr_name, input):
671 """OPTION return the HTTP Header for the attribute uri 714 """OPTION return the HTTP Header for the attribute uri
672 715
673 Returns: 716 Returns:
674 int: http status code 204 (No content) 717 int: http status code 204 (No content)
734 "HEAD, OPTIONS, GET, PUT, DELETE, PATCH" 777 "HEAD, OPTIONS, GET, PUT, DELETE, PATCH"
735 ) 778 )
736 779
737 # Call the appropriate method 780 # Call the appropriate method
738 output = None 781 output = None
739 try: 782 if resource_uri in self.db.classes:
740 if resource_uri in self.db.classes: 783 output = getattr(
741 response_code, output = getattr( 784 self, "%s_collection" % method.lower()
742 self, "%s_collection" % method.lower() 785 )(resource_uri, input)
743 )(resource_uri, input) 786 else:
787 class_name, item_id = hyperdb.splitDesignator(resource_uri)
788 if len(uri_split) == 3:
789 output = getattr(
790 self, "%s_attribute" % method.lower()
791 )(class_name, item_id, uri_split[2], input)
744 else: 792 else:
745 class_name, item_id = hyperdb.splitDesignator(resource_uri) 793 output = getattr(
746 if len(uri_split) == 3: 794 self, "%s_element" % method.lower()
747 response_code, output = getattr( 795 )(class_name, item_id, input)
748 self, "%s_attribute" % method.lower() 796
749 )(class_name, item_id, uri_split[2], input)
750 else:
751 response_code, output = getattr(
752 self, "%s_element" % method.lower()
753 )(class_name, item_id, input)
754 output = RestfulInstance.data_obj(output)
755 self.client.response_code = response_code
756 except IndexError, msg:
757 output = RestfulInstance.error_obj(404, msg)
758 self.client.response_code = 404
759 except Unauthorised, msg:
760 output = RestfulInstance.error_obj(403, msg)
761 self.client.response_code = 403
762 except (hyperdb.DesignatorError, UsageError), msg:
763 output = RestfulInstance.error_obj(400, msg)
764 self.client.response_code = 400
765 except (AttributeError, Reject), msg:
766 output = RestfulInstance.error_obj(405, msg)
767 self.client.response_code = 405
768 except ValueError, msg:
769 output = RestfulInstance.error_obj(409, msg)
770 self.client.response_code = 409
771 except NotImplementedError:
772 output = RestfulInstance.error_obj(402, 'Method under development')
773 self.client.response_code = 402
774 # nothing to pay, just a mark for debugging purpose
775 except:
776 # if self.DEBUG_MODE in roundup_server
777 # else msg = 'An error occurred. Please check...',
778 exc, val, tb = sys.exc_info()
779 output = RestfulInstance.error_obj(400, val)
780 self.client.response_code = 400
781
782 # out to the logfile, it would be nice if the server do it for me
783 print 'EXCEPTION AT', time.ctime()
784 traceback.print_exc()
785 finally:
786 if format_output.lower() == "json": 797 if format_output.lower() == "json":
787 self.client.setHeader("Content-Type", "application/json") 798 self.client.setHeader("Content-Type", "application/json")
788 if pretty_output: 799 if pretty_output:
789 indent = 4 800 indent = 4
790 else: 801 else:

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