0977ef07e047e445440a2a372698d0aac0c88e23
[releng.git] / utils / test / result_collection_api / resources / handlers.py
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 # feng.xiaowei@zte.com.cn remove ProjectHandler              5-26-2016
18 # feng.xiaowei@zte.com.cn remove TestcaseHandler             5-27-2016
19 # feng.xiaowei@zte.com.cn remove ResultHandler               5-29-2016
20 # feng.xiaowei@zte.com.cn remove DashboardHandler            5-30-2016
21 ##############################################################################
22
23 import json
24 from datetime import datetime
25
26 from tornado.web import RequestHandler, asynchronous, HTTPError
27 from tornado import gen
28
29 from models import CreateResponse
30 from common.constants import DEFAULT_REPRESENTATION, HTTP_BAD_REQUEST, \
31     HTTP_NOT_FOUND, HTTP_FORBIDDEN
32 from tornado_swagger_ui.tornado_swagger import swagger
33
34
35 class GenericApiHandler(RequestHandler):
36     def __init__(self, application, request, **kwargs):
37         super(GenericApiHandler, self).__init__(application, request, **kwargs)
38         self.db = self.settings["db"]
39         self.json_args = None
40         self.table = None
41         self.table_cls = None
42         self.db_projects = 'projects'
43         self.db_pods = 'pods'
44         self.db_testcases = 'testcases'
45         self.db_results = 'results'
46
47     def prepare(self):
48         if self.request.method != "GET" and self.request.method != "DELETE":
49             if self.request.headers.get("Content-Type") is not None:
50                 if self.request.headers["Content-Type"].startswith(
51                         DEFAULT_REPRESENTATION):
52                     try:
53                         self.json_args = json.loads(self.request.body)
54                     except (ValueError, KeyError, TypeError) as error:
55                         raise HTTPError(HTTP_BAD_REQUEST,
56                                         "Bad Json format [{}]".
57                                         format(error))
58
59     def finish_request(self, json_object=None):
60         if json_object:
61             self.write(json.dumps(json_object))
62         self.set_header("Content-Type", DEFAULT_REPRESENTATION)
63         self.finish()
64
65     def _create_response(self, resource):
66         href = self.request.full_url() + '/' + str(resource)
67         return CreateResponse(href=href).format()
68
69     def format_data(self, data):
70         cls_data = self.table_cls.from_dict(data)
71         return cls_data.format_http()
72
73     @asynchronous
74     @gen.coroutine
75     def _create(self, miss_checks, db_checks, **kwargs):
76         """
77         :param miss_checks: [miss1, miss2]
78         :param db_checks: [(table, exist, query, error)]
79         """
80         if self.json_args is None:
81             raise HTTPError(HTTP_BAD_REQUEST, "no body")
82
83         data = self.table_cls.from_dict(self.json_args)
84         for miss in miss_checks:
85             miss_data = data.__getattribute__(miss)
86             if miss_data is None or miss_data == '':
87                 raise HTTPError(HTTP_BAD_REQUEST,
88                                 '{} missing'.format(miss))
89
90         for k, v in kwargs.iteritems():
91             data.__setattr__(k, v)
92
93         for table, exist, query, error in db_checks:
94             check = yield self._eval_db_find_one(query(data), table)
95             if (exist and not check) or (not exist and check):
96                 code, message = error(data)
97                 raise HTTPError(code, message)
98
99         data.creation_date = datetime.now()
100         _id = yield self._eval_db(self.table, 'insert', data.format())
101         if 'name' in self.json_args:
102             resource = data.name
103         else:
104             resource = _id
105         self.finish_request(self._create_response(resource))
106
107     @asynchronous
108     @gen.coroutine
109     def _list(self, query=None, res_op=None, *args):
110         if query is None:
111             query = {}
112         data = []
113         cursor = self._eval_db(self.table, 'find', query)
114         while (yield cursor.fetch_next):
115             data.append(self.format_data(cursor.next_object()))
116         if res_op is None:
117             res = {self.table: data}
118         else:
119             res = res_op(data, *args)
120         self.finish_request(res)
121
122     @asynchronous
123     @gen.coroutine
124     def _get_one(self, query):
125         data = yield self._eval_db_find_one(query)
126         if data is None:
127             raise HTTPError(HTTP_NOT_FOUND,
128                             "[{}] not exist in table [{}]"
129                             .format(query, self.table))
130         self.finish_request(self.format_data(data))
131
132     @asynchronous
133     @gen.coroutine
134     def _delete(self, query):
135         data = yield self._eval_db_find_one(query)
136         if data is None:
137             raise HTTPError(HTTP_NOT_FOUND,
138                             "[{}] not exit in table [{}]"
139                             .format(query, self.table))
140
141         yield self._eval_db(self.table, 'remove', query)
142         self.finish_request()
143
144     @asynchronous
145     @gen.coroutine
146     def _update(self, query, db_keys):
147         if self.json_args is None:
148             raise HTTPError(HTTP_BAD_REQUEST, "No payload")
149
150         # check old data exist
151         from_data = yield self._eval_db_find_one(query)
152         if from_data is None:
153             raise HTTPError(HTTP_NOT_FOUND,
154                             "{} could not be found in table [{}]"
155                             .format(query, self.table))
156
157         data = self.table_cls.from_dict(from_data)
158         # check new data exist
159         equal, new_query = self._update_query(db_keys, data)
160         if not equal:
161             to_data = yield self._eval_db_find_one(new_query)
162             if to_data is not None:
163                 raise HTTPError(HTTP_FORBIDDEN,
164                                 "{} already exists in table [{}]"
165                                 .format(new_query, self.table))
166
167         # we merge the whole document """
168         edit_request = data.format()
169         edit_request.update(self._update_requests(data))
170
171         """ Updating the DB """
172         yield self._eval_db(self.table, 'update', query, edit_request)
173         edit_request['_id'] = str(data._id)
174         self.finish_request(edit_request)
175
176     def _update_requests(self, data):
177         request = dict()
178         for k, v in self.json_args.iteritems():
179             request = self._update_request(request, k, v,
180                                            data.__getattribute__(k))
181         if not request:
182             raise HTTPError(HTTP_FORBIDDEN, "Nothing to update")
183         return request
184
185     @staticmethod
186     def _update_request(edit_request, key, new_value, old_value):
187         """
188         This function serves to prepare the elements in the update request.
189         We try to avoid replace the exact values in the db
190         edit_request should be a dict in which we add an entry (key) after
191         comparing values
192         """
193         if not (new_value is None):
194             if len(new_value) > 0:
195                 if new_value != old_value:
196                     edit_request[key] = new_value
197
198         return edit_request
199
200     def _update_query(self, keys, data):
201         query = dict()
202         equal = True
203         for key in keys:
204             new = self.json_args.get(key)
205             old = data.__getattribute__(key)
206             if new is None:
207                 new = old
208             elif new != old:
209                 equal = False
210             query[key] = new
211         return equal, query
212
213     def _eval_db(self, table, method, *args):
214         return eval('self.db.%s.%s(*args)' % (table, method))
215
216     def _eval_db_find_one(self, query, table=None):
217         if table is None:
218             table = self.table
219         return self._eval_db(table, 'find_one', query)
220
221
222 class VersionHandler(GenericApiHandler):
223     @swagger.operation(nickname='list')
224     def get(self):
225         """
226             @description: list all supported versions
227             @rtype: L{Versions}
228         """
229         versions = [{'version': 'v1.0', 'description': 'basics'}]
230         self.finish_request({'versions': versions})