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 ##############################################################################
12 from tornado.web import RequestHandler, asynchronous, HTTPError
13 from tornado import gen
14 from datetime import datetime
16 from models import Pod, TestProject, TestCase, TestResult
17 from common.constants import DEFAULT_REPRESENTATION, HTTP_BAD_REQUEST, \
18 HTTP_NOT_FOUND, HTTP_FORBIDDEN
19 from common.config import prepare_put_request
22 class GenericApiHandler(RequestHandler):
24 The purpose of this class is to take benefit of inheritance and prepare
25 a set of common functions for
30 """ Prepares the database for the entire class """
31 self.db = self.settings["db"]
34 if not (self.request.method == "GET"):
35 if self.request.headers.get("Content-Type") is not None:
36 if self.request.headers["Content-Type"].startswith(
37 DEFAULT_REPRESENTATION):
39 self.json_args = json.loads(self.request.body)
40 except (ValueError, KeyError, TypeError) as error:
41 raise HTTPError(HTTP_BAD_REQUEST,
42 "Bad Json format [{}]".
47 def finish_request(self, json_object):
48 self.write(json.dumps(json_object))
49 self.set_header("Content-Type", DEFAULT_REPRESENTATION)
53 class VersionHandler(RequestHandler):
54 """ Display a message for the API version """
56 self.write("Collection of test result API, v1")
59 class PodHandler(GenericApiHandler):
60 """ Handle the requests about the POD Platforms
66 """ Prepares the database for the entire class """
67 super(PodHandler, self).initialize()
71 def get(self, pod_id=None):
73 Get all pods or a single pod
83 get_request["_id"] = int(pod_id)
86 cursor = self.db.pod.find(get_request)
87 while (yield cursor.fetch_next):
88 pod = Pod.pod_from_dict(cursor.next_object())
89 res.append(pod.format())
92 meta["total"] = len(res)
93 meta["success"] = True if len(res) > 0 else False
99 self.finish_request(answer)
102 class TestProjectHandler(GenericApiHandler):
104 TestProjectHandler Class
105 Handle the requests about the Test projects
107 - GET : Get all test projects and details about a specific one
108 - POST : Add a test project
109 - PUT : Edit test projects information (name and/or description)
110 - DELETE : Remove a test project
113 def initialize(self):
114 """ Prepares the database for the entire class """
115 super(TestProjectHandler, self).initialize()
119 def get(self, project_name=None):
125 if project_name is None:
130 if len(project_name) > 0:
131 get_request["name"] = project_name
134 cursor = self.db.test_projects.find(get_request)
135 while (yield cursor.fetch_next):
136 test_project = TestProject.testproject_from_dict(
137 cursor.next_object())
138 res.append(test_project.format_http())
141 meta["total"] = len(res)
142 meta["success"] = True if len(res) > 0 else False
145 answer["test_projects"] = res
146 answer["meta"] = meta
148 self.finish_request(answer)
153 """ Create a test project"""
155 if self.json_args is None:
156 raise HTTPError(HTTP_BAD_REQUEST)
158 query = {"name": self.json_args.get("name")}
160 # check for name in db
161 mongo_dict = yield self.db.test_projects.find_one(query)
162 if mongo_dict is not None:
163 raise HTTPError(HTTP_FORBIDDEN,
164 "{} already exists as a project".format(
165 self.json_args.get("name")))
167 test_project = TestProject.testproject_from_dict(self.json_args)
168 test_project.creation_date = datetime.now()
170 future = self.db.test_projects.insert(test_project.format())
171 result = yield future
172 test_project._id = result
174 self.finish_request(test_project.format_http())
178 def put(self, project_name):
179 """ Updates the name and description of a test project"""
181 print "PUT request for : {}".format(project_name)
183 query = {'name': project_name}
184 mongo_dict = yield self.db.test_projects.find_one(query)
185 test_project = TestProject.testproject_from_dict(mongo_dict)
186 if test_project is None:
187 raise HTTPError(HTTP_NOT_FOUND,
188 "{} could not be found".format(project_name))
190 new_name = self.json_args.get("name")
191 new_description = self.json_args.get("description")
193 # check for payload name parameter in db
194 # avoid a request if the project name has not changed in the payload
195 if new_name != test_project.name:
196 mongo_dict = yield self.db.test_projects.find_one(
198 if mongo_dict is not None:
199 raise HTTPError(HTTP_FORBIDDEN,
200 "{} already exists as a project"
203 # new dict for changes
205 request = prepare_put_request(request,
209 request = prepare_put_request(request,
212 test_project.description)
214 """ raise exception if there isn't a change """
216 raise HTTPError(HTTP_FORBIDDEN,
219 """ we merge the whole document """
220 edit_request = test_project.format()
221 edit_request.update(request)
223 """ Updating the DB """
224 res = yield self.db.test_projects.update({'name': project_name},
227 edit_request["_id"] = str(test_project._id)
229 self.finish_request({"message": "success", "content": edit_request})
233 def delete(self, project_name):
234 """ Remove a test project"""
236 print "DELETE request for : {}".format(project_name)
238 # check for an existing project to be deleted
239 mongo_dict = yield self.db.test_projects.find_one(
240 {'name': project_name})
241 test_project = TestProject.testproject_from_dict(mongo_dict)
242 if test_project is None:
243 raise HTTPError(HTTP_NOT_FOUND,
244 "{} could not be found as a project to be deleted"
245 .format(project_name))
247 # just delete it, or maybe save it elsewhere in a future
248 res = yield self.db.test_projects.remove(
249 {'name': project_name})
252 self.finish_request({"message": "success"})
255 class TestCasesHandler(GenericApiHandler):
257 TestCasesHandler Class
258 Handle the requests about the Test cases for test projects
260 - GET : Get all test cases and details about a specific one
261 - POST : Add a test project
262 - PUT : Edit test projects information (name and/or description)
265 def initialize(self):
266 """ Prepares the database for the entire class """
267 super(TestCasesHandler, self).initialize()
271 def get(self, project_name, case_name=None):
273 Get testcases(s) info
278 if case_name is None:
282 get_request["project_name"] = project_name
284 if len(case_name) > 0:
285 get_request["name"] = case_name
288 cursor = self.db.test_cases.find(get_request)
290 while (yield cursor.fetch_next):
291 test_case = TestCase.test_case_from_dict(cursor.next_object())
292 res.append(test_case.format_http())
295 meta["total"] = len(res)
296 meta["success"] = True if len(res) > 0 else False
299 answer["test_cases"] = res
300 answer["meta"] = meta
302 self.finish_request(answer)
306 def post(self, project_name):
307 """ Create a test case"""
309 print "POST Request for {}".format(project_name)
311 if self.json_args is None:
312 raise HTTPError(HTTP_BAD_REQUEST,
313 "Check your request payload")
315 # retrieve test project
316 mongo_dict = yield self.db.test_projects.find_one(
317 {"name": project_name})
318 if mongo_dict is None:
319 raise HTTPError(HTTP_FORBIDDEN,
320 "Could not find project {}"
321 .format(project_name))
323 # test_project = TestProject.testproject_from_dict(self.json_args)
325 case = TestCase.test_case_from_dict(self.json_args)
326 case.project_name = project_name
327 case.creation_date = datetime.now()
329 future = self.db.test_cases.insert(case.format())
330 result = yield future
332 self.finish_request(case.format_http())
336 def put(self, project_name, case_name):
338 Updates the name and description of a test case
339 :raises HTTPError (HTTP_NOT_FOUND, HTTP_FORBIDDEN)
342 print "PUT request for : {}/{}".format(project_name, case_name)
343 case_request = {'project_name': project_name, 'name': case_name}
345 # check if there is a case for the project in url parameters
346 mongo_dict = yield self.db.test_cases.find_one(case_request)
347 test_case = TestCase.test_case_from_dict(mongo_dict)
348 if test_case is None:
349 raise HTTPError(HTTP_NOT_FOUND,
350 "{} could not be found as a {} case to be updated"
351 .format(case_name, project_name))
353 new_name = self.json_args.get("name")
354 new_project_name = self.json_args.get("project_name")
355 new_description = self.json_args.get("description")
357 # check if there is not an existing test case
358 # with the name provided in the json payload
359 mongo_dict = yield self.db.test_cases.find_one(
360 {'project_name': new_project_name, 'name': new_name})
361 if mongo_dict is not None:
362 raise HTTPError(HTTP_FORBIDDEN,
363 "{} already exists as a project"
366 # new dict for changes
368 request = prepare_put_request(request,
372 request = prepare_put_request(request,
375 test_case.project_name)
376 request = prepare_put_request(request,
379 test_case.description)
381 # we raise an exception if there isn't a change
383 raise HTTPError(HTTP_FORBIDDEN,
386 # we merge the whole document """
387 edit_request = test_case.format()
388 edit_request.update(request)
390 """ Updating the DB """
391 res = yield self.db.test_cases.update(case_request, edit_request)
393 edit_request["_id"] = str(test_case._id)
395 self.finish_request({"message": "success", "content": edit_request})
399 def delete(self, project_name, case_name):
400 """ Remove a test case"""
402 print "DELETE request for : {}/{}".format(project_name, case_name)
403 case_request = {'project_name': project_name, 'name': case_name}
405 # check for an existing case to be deleted
406 mongo_dict = yield self.db.test_cases.find_one(case_request)
407 test_project = TestProject.testproject_from_dict(mongo_dict)
408 if test_project is None:
409 raise HTTPError(HTTP_NOT_FOUND,
410 "{}/{} could not be found as a case to be deleted"
411 .format(project_name, case_name))
413 # just delete it, or maybe save it elsewhere in a future
414 res = yield self.db.test_projects.remove(case_request)
417 self.finish_request({"message": "success"})
420 class TestResultsHandler(GenericApiHandler):
422 TestResultsHandler Class
423 Handle the requests about the Test project's results
425 - GET : Get all test results and details about a specific one
426 - POST : Add a test results
427 - DELETE : Remove a test result
430 def initialize(self):
431 """ Prepares the database for the entire class """
432 super(TestResultsHandler, self).initialize()
433 self.name = "test_result"
437 def get(self, result_id=None):
439 Retrieve result(s) for a test project on a specific POD.
440 Available filters for this request are :
441 - project : project name
445 :param result_id: Get a result by ID
448 GET /results/project=functest&case=keystone.catalog&pod=1
449 => get results with optional filters
452 project_arg = self.get_query_argument("project", None)
453 case_arg = self.get_query_argument("case", None)
454 pod_arg = self.get_query_argument("pod", None)
458 if result_id is None:
459 if project_arg is not None:
460 get_request["project_name"] = project_arg
462 if case_arg is not None:
463 get_request["case_name"] = case_arg
465 if pod_arg is not None:
466 get_request["pod_id"] = int(pod_arg)
468 get_request["_id"] = result_id
472 cursor = self.db.test_results.find(get_request)
473 while (yield cursor.fetch_next):
474 test_result = TestResult.test_result_from_dict(cursor.next_object())
475 res.append(test_result.format_http())
477 # building meta object
479 meta["total"] = len(res)
481 # final response object
483 answer["test_results"] = res
484 answer["meta"] = meta
485 self.finish_request(answer)
491 Create a new test result
492 :return: status of the request
496 # check for request payload
497 if self.json_args is None:
498 raise HTTPError(HTTP_BAD_REQUEST)
500 # check for missing parameters in the request payload
501 if self.json_args.get("project_name") is None:
502 raise HTTPError(HTTP_BAD_REQUEST)
503 if self.json_args.get("case_name") is None:
504 raise HTTPError(HTTP_BAD_REQUEST)
505 if self.json_args.get("pod_id") is None:
506 raise HTTPError(HTTP_BAD_REQUEST)
508 # TODO : replace checks with jsonschema
510 mongo_dict = yield self.db.test_projects.find_one(
511 {"name": self.json_args.get("project_name")})
512 if mongo_dict is None:
513 raise HTTPError(HTTP_NOT_FOUND,
514 "Could not find project [{}] "
515 .format(self.json_args.get("project_name")))
518 mongo_dict = yield self.db.test_cases.find_one(
519 {"name": self.json_args.get("case_name")})
520 if mongo_dict is None:
521 raise HTTPError(HTTP_NOT_FOUND,
522 "Could not find case [{}] "
523 .format(self.json_args.get("case_name")))
526 mongo_dict = yield self.db.pod.find_one(
527 {"_id": self.json_args.get("pod_id")})
528 if mongo_dict is None:
529 raise HTTPError(HTTP_NOT_FOUND,
530 "Could not find POD [{}] "
531 .format(self.json_args.get("pod_id")))
533 # convert payload to object
534 test_result = TestResult.test_result_from_dict(self.json_args)
535 test_result.creation_date = datetime.now()
537 future = self.db.test_results.insert(test_result.format())
538 result = yield future
539 test_result._id = result
541 self.finish_request(test_result.format_http())