1 ##############################################################################
2 # Copyright (c) 2015 Orange
3 # guyrodrigue.koffi@orange.com / koffirodrigue@gmail.com
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 # feng.xiaowei@zte.com.cn refactor db.pod to db.pods 5-19-2016
9 # feng.xiaowei@zte.com.cn refactor test_project to project 5-19-2016
10 # feng.xiaowei@zte.com.cn refactor response body 5-19-2016
11 # feng.xiaowei@zte.com.cn refactor pod/project response info 5-19-2016
12 # feng.xiaowei@zte.com.cn refactor testcase related handler 5-20-2016
13 # feng.xiaowei@zte.com.cn refactor result related handler 5-23-2016
14 # feng.xiaowei@zte.com.cn refactor dashboard related handler 5-24-2016
15 # feng.xiaowei@zte.com.cn add methods to GenericApiHandler 5-26-2016
16 # feng.xiaowei@zte.com.cn remove PodHandler 5-26-2016
17 ##############################################################################
20 from datetime import datetime, timedelta
22 from tornado.web import RequestHandler, asynchronous, HTTPError
23 from tornado import gen
25 from models import CreateResponse
26 from resources.result_models import TestResult
27 from common.constants import DEFAULT_REPRESENTATION, HTTP_BAD_REQUEST, \
28 HTTP_NOT_FOUND, HTTP_FORBIDDEN
29 from common.config import prepare_put_request
30 from dashboard.dashboard_utils import check_dashboard_ready_project, \
31 check_dashboard_ready_case, get_dashboard_result
34 def format_data(data, cls):
35 cls_data = cls.from_dict(data)
36 return cls_data.format_http()
39 class GenericApiHandler(RequestHandler):
40 def __init__(self, application, request, **kwargs):
41 super(GenericApiHandler, self).__init__(application, request, **kwargs)
42 self.db = self.settings["db"]
46 self.db_projects = 'projects'
48 self.db_testcases = 'testcases'
49 self.db_results = 'results'
52 if self.request.method != "GET" and self.request.method != "DELETE":
53 if self.request.headers.get("Content-Type") is not None:
54 if self.request.headers["Content-Type"].startswith(
55 DEFAULT_REPRESENTATION):
57 self.json_args = json.loads(self.request.body)
58 except (ValueError, KeyError, TypeError) as error:
59 raise HTTPError(HTTP_BAD_REQUEST,
60 "Bad Json format [{}]".
63 def finish_request(self, json_object=None):
65 self.write(json.dumps(json_object))
66 self.set_header("Content-Type", DEFAULT_REPRESENTATION)
69 def _create_response(self, resource):
70 href = self.request.full_url() + '/' + str(resource)
71 return CreateResponse(href=href).format()
75 def _create(self, miss_checks, db_checks, **kwargs):
77 :param miss_checks: [miss1, miss2]
78 :param db_checks: [(table, exist, query, (error, message))]
79 :param db_op: (insert/remove)
80 :param res_op: (_create_response/None)
83 if self.json_args is None:
84 raise HTTPError(HTTP_BAD_REQUEST, "no body")
86 data = self.table_cls.from_dict(self.json_args)
87 for miss in miss_checks:
88 miss_data = data.__getattribute__(miss)
89 if miss_data is None or miss_data == '':
90 raise HTTPError(HTTP_BAD_REQUEST,
91 '{} missing'.format(miss))
93 for k, v in kwargs.iteritems():
94 data.__setattr__(k, v)
96 for table, exist, query, error in db_checks:
97 check = yield self._eval_db(table, 'find_one', query(data))
98 if (exist and not check) or (not exist and check):
99 code, message = error(data)
100 raise HTTPError(code, message)
102 data.creation_date = datetime.now()
103 _id = yield self._eval_db(self.table, 'insert', data.format())
104 if 'name' in self.json_args:
108 self.finish_request(self._create_response(resource))
112 def _list(self, query=None):
116 cursor = self._eval_db(self.table, 'find', query)
117 while (yield cursor.fetch_next):
118 res.append(format_data(cursor.next_object(), self.table_cls))
119 self.finish_request({self.table: res})
123 def _get_one(self, query):
124 data = yield self._eval_db(self.table, 'find_one', query)
126 raise HTTPError(HTTP_NOT_FOUND,
127 "[{}] not exist in table [{}]"
128 .format(query, self.table))
129 self.finish_request(format_data(data, self.table_cls))
133 def _delete(self, query):
134 data = yield self._eval_db(self.table, 'find_one', query)
136 raise HTTPError(HTTP_NOT_FOUND,
137 "[{}] not exit in table [{}]"
138 .format(query, self.table))
140 yield self._eval_db(self.table, 'remove', query)
141 self.finish_request()
145 def _update(self, query, db_keys):
146 if self.json_args is None:
147 raise HTTPError(HTTP_BAD_REQUEST, "No payload")
149 # check old data exist
150 from_data = yield self._eval_db(self.table, 'find_one', query)
151 if from_data is None:
152 raise HTTPError(HTTP_NOT_FOUND,
153 "{} could not be found in table [{}]"
154 .format(query, self.table))
156 data = self.table_cls.from_dict(from_data)
157 # check new data exist
158 equal, new_query = self._update_query(db_keys, data)
160 to_data = yield self._eval_db(self.table, 'find_one', new_query)
161 if to_data is not None:
162 raise HTTPError(HTTP_FORBIDDEN,
163 "{} already exists in table [{}]"
164 .format(new_query, self.table))
166 # we merge the whole document """
167 edit_request = data.format()
168 edit_request.update(self._update_request(data))
170 """ Updating the DB """
171 yield self._eval_db(self.table, 'update', query, edit_request)
172 edit_request['_id'] = str(data._id)
173 self.finish_request(edit_request)
175 def _update_request(self, data):
177 for k, v in self.json_args.iteritems():
178 request = prepare_put_request(request, k, v,
179 data.__getattribute__(k))
181 raise HTTPError(HTTP_FORBIDDEN, "Nothing to update")
184 def _update_query(self, keys, data):
188 new = self.json_args.get(key)
189 old = data.__getattribute__(key)
197 def _eval_db(self, table, method, *args):
198 return eval('self.db.%s.%s(*args)' % (table, method))
201 class VersionHandler(GenericApiHandler):
202 """ Display a message for the API version """
204 self.finish_request([{'v1': 'basics'}])
207 class DashboardHandler(GenericApiHandler):
209 DashboardHandler Class
210 Handle the requests about the Test project's results
211 in a dahboard ready format
213 - GET : Get all test results and details about a specific one
215 def initialize(self):
216 """ Prepares the database for the entire class """
217 super(DashboardHandler, self).initialize()
218 self.name = "dashboard"
224 Retrieve dashboard ready result(s) for a test project
225 Available filters for this request are :
226 - project : project name
229 - version : platform version (Arno-R1, ...)
230 - installer (fuel, ...)
231 - period : x (x last days)
234 :param result_id: Get a result by ID
237 GET /dashboard?project=functest&case=vPing&version=Arno-R1 \
238 &pod=pod_name&period=15
239 => get results with optional filters
242 project_arg = self.get_query_argument("project", None)
243 case_arg = self.get_query_argument("case", None)
244 pod_arg = self.get_query_argument("pod", None)
245 version_arg = self.get_query_argument("version", None)
246 installer_arg = self.get_query_argument("installer", None)
247 period_arg = self.get_query_argument("period", None)
252 if project_arg is not None:
253 query["project_name"] = project_arg
255 if case_arg is not None:
256 query["case_name"] = case_arg
258 if pod_arg is not None:
259 query["pod_name"] = pod_arg
261 if version_arg is not None:
262 query["version"] = version_arg
264 if installer_arg is not None:
265 query["installer"] = installer_arg
267 if period_arg is not None:
269 period_arg = int(period_arg)
271 raise HTTPError(HTTP_BAD_REQUEST)
273 period = datetime.now() - timedelta(days=period_arg)
274 obj = {"$gte": str(period)}
275 query["creation_date"] = obj
277 # on /dashboard retrieve the list of projects and testcases
278 # ready for dashboard
279 if project_arg is None:
280 raise HTTPError(HTTP_NOT_FOUND, "Project name missing")
282 if not check_dashboard_ready_project(project_arg):
283 raise HTTPError(HTTP_NOT_FOUND,
284 'Project [{}] not dashboard ready'
285 .format(project_arg))
290 'Test case missing for project [{}]'.format(project_arg))
292 if not check_dashboard_ready_case(project_arg, case_arg):
295 'Test case [{}] not dashboard ready for project [{}]'
296 .format(case_arg, project_arg))
298 # special case of status for project
300 if case_arg != "status":
301 cursor = self.db.results.find(query)
302 while (yield cursor.fetch_next):
303 result = TestResult.from_dict(cursor.next_object())
304 res.append(result.format_http())
306 # final response object
307 self.finish_request(get_dashboard_result(project_arg, case_arg, res))