update projects in scenario
[releng.git] / utils / test / testapi / opnfv_testapi / resources / handlers.py
index 522bbe7..240fa0a 100644 (file)
 # feng.xiaowei@zte.com.cn remove DashboardHandler            5-30-2016
 ##############################################################################
 
-from datetime import datetime
-import functools
 import json
+from datetime import datetime
 
 from tornado import gen
 from tornado import web
 
-import models
+from opnfv_testapi.common import check
 from opnfv_testapi.common import message
 from opnfv_testapi.common import raises
+from opnfv_testapi.db import api as dbapi
+from opnfv_testapi.resources import models
 from opnfv_testapi.tornado_swagger import swagger
 
 DEFAULT_REPRESENTATION = "application/json"
@@ -38,7 +39,6 @@ DEFAULT_REPRESENTATION = "application/json"
 class GenericApiHandler(web.RequestHandler):
     def __init__(self, application, request, **kwargs):
         super(GenericApiHandler, self).__init__(application, request, **kwargs)
-        self.db = self.settings["db"]
         self.json_args = None
         self.table = None
         self.table_cls = None
@@ -50,7 +50,7 @@ class GenericApiHandler(web.RequestHandler):
         self.auth = self.settings["auth"]
 
     def prepare(self):
-        if self.request.method != "GET" and self.request.method != "DELETE":
+        if self.request.body:
             if self.request.headers.get("Content-Type") is not None:
                 if self.request.headers["Content-Type"].startswith(
                         DEFAULT_REPRESENTATION):
@@ -73,52 +73,24 @@ class GenericApiHandler(web.RequestHandler):
         cls_data = self.table_cls.from_dict(data)
         return cls_data.format_http()
 
-    def authenticate(method):
-        @web.asynchronous
-        @gen.coroutine
-        @functools.wraps(method)
-        def wrapper(self, *args, **kwargs):
-            if self.auth:
-                try:
-                    token = self.request.headers['X-Auth-Token']
-                except KeyError:
-                    raises.Unauthorized(message.unauthorized())
-                query = {'access_token': token}
-                check = yield self._eval_db_find_one(query, 'tokens')
-                if not check:
-                    raises.Forbidden(message.invalid_token())
-            ret = yield gen.coroutine(method)(self, *args, **kwargs)
-            raise gen.Return(ret)
-        return wrapper
-
-    @authenticate
-    def _create(self, miss_checks, db_checks, **kwargs):
+    @check.authenticate
+    @check.no_body
+    @check.miss_fields
+    @check.carriers_exist
+    @check.new_not_exists
+    def _create(self, **kwargs):
         """
         :param miss_checks: [miss1, miss2]
         :param db_checks: [(table, exist, query, error)]
         """
-        if self.json_args is None:
-            raises.BadRequest(message.no_body())
-
         data = self.table_cls.from_dict(self.json_args)
-        for miss in miss_checks:
-            miss_data = data.__getattribute__(miss)
-            if miss_data is None or miss_data == '':
-                raises.BadRequest(message.missing(miss))
-
         for k, v in kwargs.iteritems():
-            data.__setattr__(k, v)
-
-        for table, exist, query, error in db_checks:
-            check = yield self._eval_db_find_one(query(data), table)
-            if (exist and not check) or (not exist and check):
-                code, msg = error(data)
-                raises.CodeTBD(code, msg)
+            if k != 'query':
+                data.__setattr__(k, v)
 
         if self.table != 'results':
             data.creation_date = datetime.now()
-        _id = yield self._eval_db(self.table, 'insert', data.format(),
-                                  check_keys=False)
+        _id = yield dbapi.db_save(self.table, data.format())
         if 'name' in self.json_args:
             resource = data.name
         else:
@@ -128,65 +100,101 @@ class GenericApiHandler(web.RequestHandler):
     @web.asynchronous
     @gen.coroutine
     def _list(self, query=None, res_op=None, *args, **kwargs):
+        sort = kwargs.get('sort')
+        page = kwargs.get('page', 0)
+        last = kwargs.get('last', 0)
+        per_page = kwargs.get('per_page', 0)
         if query is None:
             query = {}
-        data = []
-        cursor = self._eval_db(self.table, 'find', query)
-        if 'sort' in kwargs:
-            cursor = cursor.sort(kwargs.get('sort'))
-        if 'last' in kwargs:
-            cursor = cursor.limit(kwargs.get('last'))
+
+        total_pages = 0
+        if page > 0:
+            cursor = dbapi.db_list(self.table, query)
+            records_count = yield cursor.count()
+            total_pages = self._calc_total_pages(records_count,
+                                                 last,
+                                                 page,
+                                                 per_page)
+        pipelines = self._set_pipelines(query, sort, last, page, per_page)
+        cursor = dbapi.db_aggregate(self.table, pipelines)
+        data = list()
         while (yield cursor.fetch_next):
             data.append(self.format_data(cursor.next_object()))
         if res_op is None:
             res = {self.table: data}
         else:
             res = res_op(data, *args)
+        if page > 0:
+            res.update({
+                'pagination': {
+                    'current_page': kwargs.get('page'),
+                    'total_pages': total_pages
+                }
+            })
         self.finish_request(res)
 
+    @staticmethod
+    def _calc_total_pages(records_count, last, page, per_page):
+        records_nr = records_count
+        if (records_count > last) and (last > 0):
+            records_nr = last
+
+        total_pages, remainder = divmod(records_nr, per_page)
+        if remainder > 0:
+            total_pages += 1
+        if page > 1 and page > total_pages:
+            raises.BadRequest(
+                'Request page > total_pages [{}]'.format(total_pages))
+        return total_pages
+
+    @staticmethod
+    def _set_pipelines(query, sort, last, page, per_page):
+        pipelines = list()
+        if query:
+            pipelines.append({'$match': query})
+        if sort:
+            pipelines.append({'$sort': sort})
+
+        if page > 0:
+            pipelines.append({'$skip': (page - 1) * per_page})
+            pipelines.append({'$limit': per_page})
+        elif last > 0:
+            pipelines.append({'$limit': last})
+
+        return pipelines
+
     @web.asynchronous
     @gen.coroutine
-    def _get_one(self, query):
-        data = yield self._eval_db_find_one(query)
-        if data is None:
-            raises.NotFound(message.not_found(self.table, query))
+    @check.not_exist
+    def _get_one(self, data, query=None):
         self.finish_request(self.format_data(data))
 
-    @authenticate
-    def _delete(self, query):
-        data = yield self._eval_db_find_one(query)
-        if data is None:
-            raises.NotFound(message.not_found(self.table, query))
-
-        yield self._eval_db(self.table, 'remove', query)
+    @check.authenticate
+    @check.not_exist
+    def _delete(self, data, query=None):
+        yield dbapi.db_delete(self.table, query)
         self.finish_request()
 
-    @authenticate
-    def _update(self, query, db_keys):
-        if self.json_args is None:
-            raises.BadRequest(message.no_body())
-
-        # check old data exist
-        from_data = yield self._eval_db_find_one(query)
-        if from_data is None:
-            raises.NotFound(message.not_found(self.table, query))
-
-        data = self.table_cls.from_dict(from_data)
-        # check new data exist
-        equal, new_query = self._update_query(db_keys, data)
-        if not equal:
-            to_data = yield self._eval_db_find_one(new_query)
-            if to_data is not None:
-                raises.Forbidden(message.exist(self.table, new_query))
-
-        # we merge the whole document """
-        edit_request = self._update_requests(data)
-
-        """ Updating the DB """
-        yield self._eval_db(self.table, 'update', query, edit_request,
-                            check_keys=False)
-        edit_request['_id'] = str(data._id)
-        self.finish_request(edit_request)
+    @check.authenticate
+    @check.no_body
+    @check.not_exist
+    @check.updated_one_not_exist
+    def _update(self, data, query=None, **kwargs):
+        data = self.table_cls.from_dict(data)
+        update_req = self._update_requests(data)
+        yield dbapi.db_update(self.table, query, update_req)
+        update_req['_id'] = str(data._id)
+        self.finish_request(update_req)
+
+    @check.authenticate
+    @check.no_body
+    @check.not_exist
+    @check.updated_one_not_exist
+    def pure_update(self, data, query=None, **kwargs):
+        data = self.table_cls.from_dict(data)
+        update_req = self._update_requests(data)
+        yield dbapi.db_update(self.table, query, update_req)
+        self.finish_request()
 
     def _update_requests(self, data):
         request = dict()
@@ -219,22 +227,13 @@ class GenericApiHandler(web.RequestHandler):
         equal = True
         for key in keys:
             new = self.json_args.get(key)
-            old = data.__getattribute__(key)
+            old = data.get(key)
             if new is None:
                 new = old
             elif new != old:
                 equal = False
             query[key] = new
-        return equal, query
-
-    def _eval_db(self, table, method, *args, **kwargs):
-        exec_collection = self.db.__getattr__(table)
-        return exec_collection.__getattribute__(method)(*args, **kwargs)
-
-    def _eval_db_find_one(self, query, table=None):
-        if table is None:
-            table = self.table
-        return self._eval_db(table, 'find_one', query)
+        return query if not equal else dict()
 
 
 class VersionHandler(GenericApiHandler):