comparison roundup/rest.py @ 5596:55fa81de6a57 REST-rebased

Added summary page Change data uri of class methods from /rest/class to /rest/data/class committer: Ralf Schlatterbeck <rsc@runtux.com>
author Chau Nguyen <dangchau1991@yahoo.com>
date Wed, 30 Jan 2019 10:26:35 +0100
parents 65caddd54da2
children de9933cfcfc4
comparison
equal deleted inserted replaced
5595:65caddd54da2 5596:55fa81de6a57
12 import sys 12 import sys
13 import time 13 import time
14 import traceback 14 import traceback
15 import xml 15 import xml
16 from roundup import hyperdb 16 from roundup import hyperdb
17 from roundup import date
17 from roundup.exceptions import * 18 from roundup.exceptions import *
18 19
19 20
20 def _data_decorator(func): 21 def _data_decorator(func):
21 """Wrap the returned data into an object.""" 22 """Wrap the returned data into an object."""
68 'data': data 69 'data': data
69 } 70 }
70 return result 71 return result
71 return format_object 72 return format_object
72 73
74
73 def parse_accept_header(accept): 75 def parse_accept_header(accept):
74 """ 76 """
75 Parse the Accept header *accept*, returning a list with 3-tuples of 77 Parse the Accept header *accept*, returning a list with 3-tuples of
76 [(str(media_type), dict(params), float(q_value)),] ordered by q values. 78 [(str(media_type), dict(params), float(q_value)),] ordered by q values.
77 79
109 # add the version as a media param 111 # add the version as a media param
110 try: 112 try:
111 version = media_params.append(('version', 113 version = media_params.append(('version',
112 float(rest))) 114 float(rest)))
113 except ValueError: 115 except ValueError:
114 version = 1.0 # could not be parsed 116 version = 1.0 # could not be parsed
115 # add the vendor code as a media param 117 # add the vendor code as a media param
116 media_params.append(('vendor', vnd)) 118 media_params.append(('vendor', vnd))
117 # and re-write media_type to something like application/json so 119 # and re-write media_type to something like application/json so
118 # it can be used usefully when looking up emitters 120 # it can be used usefully when looking up emitters
119 media_type = '{}/{}'.format(typ, extra) 121 media_type = '{}/{}'.format(typ, extra)
127 else: 129 else:
128 media_params.append((key, value)) 130 media_params.append((key, value))
129 result.append((media_type, dict(media_params), q)) 131 result.append((media_type, dict(media_params), q))
130 result.sort(lambda x, y: -cmp(x[2], y[2])) 132 result.sort(lambda x, y: -cmp(x[2], y[2]))
131 return result 133 return result
134
132 135
133 class RestfulInstance(object): 136 class RestfulInstance(object):
134 """The RestfulInstance performs REST request from the client""" 137 """The RestfulInstance performs REST request from the client"""
135 138
136 __default_patch_op = "replace" # default operator for PATCH method 139 __default_patch_op = "replace" # default operator for PATCH method
875 "Accept-Patch", 878 "Accept-Patch",
876 "application/x-www-form-urlencoded, multipart/form-data" 879 "application/x-www-form-urlencoded, multipart/form-data"
877 ) 880 )
878 return 204, "" 881 return 204, ""
879 882
883 @_data_decorator
884 def summary(self, input):
885 """Get a summary of resource from class URI.
886
887 This function returns only items have View permission
888 class_name should be valid already
889
890 Args:
891 class_name (string): class name of the resource (Ex: issue, msg)
892 input (list): the submitted form of the user
893
894 Returns:
895 int: http status code 200 (OK)
896 list:
897 """
898 if not self.db.security.hasPermission(
899 'View', self.db.getuid(), 'issue'
900 ) and not self.db.security.hasPermission(
901 'View', self.db.getuid(), 'status'
902 ) and not self.db.security.hasPermission(
903 'View', self.db.getuid(), 'issue'
904 ):
905 raise Unauthorised('Permission to view summary denied')
906
907 old = date.Date('-1w')
908
909 created = []
910 summary = {}
911 messages = []
912
913 # loop through all the recently-active issues
914 for issue_id in self.db.issue.filter(None, {'activity': '-1w;'}):
915 num = 0
916 status_name = self.db.status.get(
917 self.db.issue.get(issue_id, 'status'),
918 'name'
919 )
920 issue_object = {
921 'id': issue_id,
922 'link': self.base_path + 'issue' + issue_id,
923 'title': self.db.issue.get(issue_id, 'title')
924 }
925 for x, ts, uid, action, data in self.db.issue.history(issue_id):
926 if ts < old:
927 continue
928 if action == 'create':
929 created.append(issue_object)
930 elif action == 'set' and 'messages' in data:
931 num += 1
932 summary.setdefault(status_name, []).append(issue_object)
933 messages.append((num, issue_object))
934
935 messages.sort(reverse=True)
936
937 result = {
938 'created': created,
939 'summary': summary,
940 'most_discussed': messages[:10]
941 }
942
943 return 200, result
944
880 def dispatch(self, method, uri, input): 945 def dispatch(self, method, uri, input):
881 """format and process the request""" 946 """format and process the request"""
882 # if X-HTTP-Method-Override is set, follow the override method 947 # if X-HTTP-Method-Override is set, follow the override method
883 headers = self.client.request.headers 948 headers = self.client.request.headers
884 method = headers.getheader('X-HTTP-Method-Override') or method 949 method = headers.getheader('X-HTTP-Method-Override') or method
918 "HEAD, OPTIONS, GET, PUT, DELETE, PATCH" 983 "HEAD, OPTIONS, GET, PUT, DELETE, PATCH"
919 ) 984 )
920 985
921 # PATH is split to multiple pieces 986 # PATH is split to multiple pieces
922 # 0 - rest 987 # 0 - rest
923 # 1 - resource 988 # 1 - data
924 # 2 - attribute 989 # 2 - resource
925 uri_split = uri.split("/") 990 # 3 - attribute
926 resource_uri = uri_split[1] 991 uri_split = uri.lower().split("/")
927
928 try:
929 class_name, item_id = hyperdb.splitDesignator(resource_uri)
930 except hyperdb.DesignatorError:
931 class_name = resource_uri
932 item_id = None
933 992
934 # Call the appropriate method 993 # Call the appropriate method
935 if (class_name not in self.db.classes) or (len(uri_split) > 3): 994 if len(uri_split) == 2 and uri_split[1] == 'summary':
995 output = self.summary(input)
996 elif 4 >= len(uri_split) > 2 and uri_split[1] == 'data':
997 resource_uri = uri_split[2]
998 try:
999 class_name, item_id = hyperdb.splitDesignator(resource_uri)
1000 except hyperdb.DesignatorError:
1001 class_name = resource_uri
1002 item_id = None
1003
1004 if class_name not in self.db.classes:
1005 output = self.error_obj(404, "Not found")
1006 elif item_id is None:
1007 if len(uri_split) == 3:
1008 output = getattr(
1009 self, "%s_collection" % method.lower()
1010 )(class_name, input)
1011 else:
1012 output = self.error_obj(404, "Not found")
1013 else:
1014 if len(uri_split) == 3:
1015 output = getattr(
1016 self, "%s_element" % method.lower()
1017 )(class_name, item_id, input)
1018 else:
1019 output = getattr(
1020 self, "%s_attribute" % method.lower()
1021 )(class_name, item_id, uri_split[3], input)
1022 else:
936 output = self.error_obj(404, "Not found") 1023 output = self.error_obj(404, "Not found")
937 elif item_id is None:
938 if len(uri_split) == 2:
939 output = getattr(
940 self, "%s_collection" % method.lower()
941 )(class_name, input)
942 else:
943 if len(uri_split) == 2:
944 output = getattr(
945 self, "%s_element" % method.lower()
946 )(class_name, item_id, input)
947 else:
948 output = getattr(
949 self, "%s_attribute" % method.lower()
950 )(class_name, item_id, uri_split[2], input)
951 1024
952 # Format the content type 1025 # Format the content type
953 if data_type.lower() == "json": 1026 if data_type.lower() == "json":
954 self.client.setHeader("Content-Type", "application/json") 1027 self.client.setHeader("Content-Type", "application/json")
955 if pretty_output: 1028 if pretty_output:

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