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