summaryrefslogtreecommitdiffstats
path: root/utils/test/testapi/opnfv_testapi/resources
diff options
context:
space:
mode:
Diffstat (limited to 'utils/test/testapi/opnfv_testapi/resources')
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/handlers.py107
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/models.py23
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/result_handlers.py94
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/result_models.py8
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/scenario_handlers.py703
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/scenario_models.py52
6 files changed, 818 insertions, 169 deletions
diff --git a/utils/test/testapi/opnfv_testapi/resources/handlers.py b/utils/test/testapi/opnfv_testapi/resources/handlers.py
index 2fc31ca45..ed55c7028 100644
--- a/utils/test/testapi/opnfv_testapi/resources/handlers.py
+++ b/utils/test/testapi/opnfv_testapi/resources/handlers.py
@@ -20,8 +20,8 @@
# feng.xiaowei@zte.com.cn remove DashboardHandler 5-30-2016
##############################################################################
-from datetime import datetime
import json
+from datetime import datetime
from tornado import gen
from tornado import web
@@ -29,6 +29,7 @@ from tornado import web
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
@@ -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):
@@ -90,8 +90,7 @@ class GenericApiHandler(web.RequestHandler):
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:
@@ -101,22 +100,72 @@ 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'))
- while (yield cursor.fetch_next):
- data.append(self.format_data(cursor.next_object()))
+ pipelines = list()
+ pipelines.append({'$match': query})
+
+ total_pages = 0
+ data = list()
+ cursor = dbapi.db_list(self.table, query)
+ records_count = yield cursor.count()
+ if records_count > 0:
+ if page > 0:
+ total_pages, return_nr = self._calc_total_pages(records_count,
+ last,
+ page,
+ per_page)
+ pipelines = self._set_pipelines(pipelines,
+ sort,
+ return_nr,
+ page,
+ per_page)
+ cursor = dbapi.db_aggregate(self.table, pipelines)
+ 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, records_nr
+
+ @staticmethod
+ def _set_pipelines(pipelines, sort, return_nr, page, per_page):
+ if sort:
+ pipelines.append({'$sort': sort})
+
+ over = (page - 1) * per_page
+ left = return_nr - over
+ pipelines.append({'$skip': over})
+ pipelines.append({'$limit': per_page if per_page < left else left})
+
+ return pipelines
+
@web.asynchronous
@gen.coroutine
@check.not_exist
@@ -126,7 +175,7 @@ class GenericApiHandler(web.RequestHandler):
@check.authenticate
@check.not_exist
def _delete(self, data, query=None):
- yield self._eval_db(self.table, 'remove', query)
+ yield dbapi.db_delete(self.table, query)
self.finish_request()
@check.authenticate
@@ -136,11 +185,20 @@ class GenericApiHandler(web.RequestHandler):
def _update(self, data, query=None, **kwargs):
data = self.table_cls.from_dict(data)
update_req = self._update_requests(data)
- yield self._eval_db(self.table, 'update', query, update_req,
- check_keys=False)
+ 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()
for k, v in self.json_args.iteritems():
@@ -180,23 +238,6 @@ class GenericApiHandler(web.RequestHandler):
query[key] = new
return query if not equal else dict()
- 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)
-
- def db_save(self, collection, data):
- self._eval_db(collection, 'insert', data, check_keys=False)
-
- def db_find_one(self, query, collection=None):
- if not collection:
- collection = self.table
- return self._eval_db(collection, 'find_one', query)
-
class VersionHandler(GenericApiHandler):
@swagger.operation(nickname='listAllVersions')
diff --git a/utils/test/testapi/opnfv_testapi/resources/models.py b/utils/test/testapi/opnfv_testapi/resources/models.py
index e8fc532b7..e70a6ed23 100644
--- a/utils/test/testapi/opnfv_testapi/resources/models.py
+++ b/utils/test/testapi/opnfv_testapi/resources/models.py
@@ -48,6 +48,29 @@ class ModelBase(object):
return t
+ @classmethod
+ def from_dict_with_raise(cls, a_dict):
+ if a_dict is None:
+ return None
+
+ attr_parser = cls.attr_parser()
+ t = cls()
+ for k, v in a_dict.iteritems():
+ if k not in t.__dict__:
+ raise AttributeError(
+ '{} has no attribute {}'.format(cls.__name__, k))
+ value = v
+ if isinstance(v, dict) and k in attr_parser:
+ value = attr_parser[k].from_dict_with_raise(v)
+ elif isinstance(v, list) and k in attr_parser:
+ value = []
+ for item in v:
+ value.append(attr_parser[k].from_dict_with_raise(item))
+
+ t.__setattr__(k, value)
+
+ return t
+
@staticmethod
def attr_parser():
return {}
diff --git a/utils/test/testapi/opnfv_testapi/resources/result_handlers.py b/utils/test/testapi/opnfv_testapi/resources/result_handlers.py
index 824a89e58..9389d266d 100644
--- a/utils/test/testapi/opnfv_testapi/resources/result_handlers.py
+++ b/utils/test/testapi/opnfv_testapi/resources/result_handlers.py
@@ -6,16 +6,20 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
+import logging
from datetime import datetime
from datetime import timedelta
+import json
from bson import objectid
+from opnfv_testapi.common.config import CONF
from opnfv_testapi.common import message
from opnfv_testapi.common import raises
from opnfv_testapi.resources import handlers
from opnfv_testapi.resources import result_models
from opnfv_testapi.tornado_swagger import swagger
+from opnfv_testapi.ui.auth import constants as auth_const
class GenericResultHandler(handlers.GenericApiHandler):
@@ -37,6 +41,7 @@ class GenericResultHandler(handlers.GenericApiHandler):
query = dict()
date_range = dict()
+ query['public'] = {'$not': {'$eq': 'false'}}
for k in self.request.query_arguments.keys():
v = self.get_query_argument(k)
if k == 'project' or k == 'pod' or k == 'case':
@@ -53,10 +58,24 @@ class GenericResultHandler(handlers.GenericApiHandler):
date_range.update({'$gte': str(v)})
elif k == 'to':
date_range.update({'$lt': str(v)})
- elif k != 'last' and k != 'page':
+ elif k == 'signed':
+ openid = self.get_secure_cookie(auth_const.OPENID)
+ role = self.get_secure_cookie(auth_const.ROLE)
+ logging.info('role:%s', role)
+ if role:
+ del query['public']
+ if role != "reviewer":
+ query['user'] = openid
+ elif k not in ['last', 'page', 'descend']:
query[k] = v
if date_range:
query['start_date'] = date_range
+
+ # if $lt is not provided,
+ # empty/None/null/'' start_date will also be returned
+ if 'start_date' in query and '$lt' not in query['start_date']:
+ query['start_date'].update({'$lt': str(datetime.now())})
+
return query
@@ -81,9 +100,10 @@ class ResultsCLHandler(GenericResultHandler):
- criteria : the global criteria status passed or failed
- trust_indicator : evaluate the stability of the test case
to avoid running systematically long and stable test case
+ - signed : get logined user result
GET /results/project=functest&case=vPing&version=Arno-R1 \
- &pod=pod_name&period=15
+ &pod=pod_name&period=15&signed
@return 200: all test results consist with query,
empty list if no result is found
@rtype: L{TestResults}
@@ -135,22 +155,41 @@ class ResultsCLHandler(GenericResultHandler):
@type last: L{string}
@in last: query
@required last: False
+ @param page: which page to list, default to 1
+ @type page: L{int}
+ @in page: query
+ @required page: False
@param trust_indicator: must be float
@type trust_indicator: L{float}
@in trust_indicator: query
@required trust_indicator: False
+ @param signed: user results or all results
+ @type signed: L{string}
+ @in signed: query
+ @required signed: False
+ @param descend: true, newest2oldest; false, oldest2newest
+ @type descend: L{string}
+ @in descend: query
+ @required descend: False
"""
- last = self.get_query_argument('last', 0)
- if last is not None:
- last = self.get_int('last', last)
+ def descend_limit():
+ descend = self.get_query_argument('descend', 'true')
+ return -1 if descend.lower() == 'true' else 1
+
+ def last_limit():
+ return self.get_int('last', self.get_query_argument('last', 0))
- page = self.get_query_argument('page', 0)
- if page:
- last = 20
+ def page_limit():
+ return self.get_int('page', self.get_query_argument('page', 1))
- self._list(query=self.set_query(),
- sort=[('start_date', -1)],
- last=last)
+ limitations = {
+ 'sort': {'_id': descend_limit()},
+ 'last': last_limit(),
+ 'page': page_limit(),
+ 'per_page': CONF.api_results_per_page
+ }
+
+ self._list(query=self.set_query(), **limitations)
@swagger.operation(nickname="createTestResult")
def post(self):
@@ -164,6 +203,9 @@ class ResultsCLHandler(GenericResultHandler):
@raise 404: pod/project/testcase not exist
@raise 400: body/pod_name/project_name/case_name not provided
"""
+ self._post()
+
+ def _post(self):
def pod_query():
return {'name': self.json_args.get('pod_name')}
@@ -178,9 +220,39 @@ class ResultsCLHandler(GenericResultHandler):
carriers = [('pods', pod_query),
('projects', project_query),
('testcases', testcase_query)]
+
self._create(miss_fields=miss_fields, carriers=carriers)
+class ResultsUploadHandler(ResultsCLHandler):
+ @swagger.operation(nickname="uploadTestResult")
+ def post(self):
+ """
+ @description: upload and create a test result
+ @param body: result to be created
+ @type body: L{ResultCreateRequest}
+ @in body: body
+ @rtype: L{CreateResponse}
+ @return 200: result is created.
+ @raise 404: pod/project/testcase not exist
+ @raise 400: body/pod_name/project_name/case_name not provided
+ """
+ logging.info('file upload')
+ fileinfo = self.request.files['file'][0]
+ is_public = self.get_body_argument('public')
+ logging.warning('public:%s', is_public)
+ logging.info('results is :%s', fileinfo['filename'])
+ logging.info('results is :%s', fileinfo['body'])
+ self.json_args = json.loads(fileinfo['body']).copy()
+ self.json_args['public'] = is_public
+
+ openid = self.get_secure_cookie(auth_const.OPENID)
+ if openid:
+ self.json_args['user'] = openid
+
+ super(ResultsUploadHandler, self)._post()
+
+
class ResultsGURHandler(GenericResultHandler):
@swagger.operation(nickname='getTestResultById')
def get(self, result_id):
diff --git a/utils/test/testapi/opnfv_testapi/resources/result_models.py b/utils/test/testapi/opnfv_testapi/resources/result_models.py
index 62a6dacff..890bf8220 100644
--- a/utils/test/testapi/opnfv_testapi/resources/result_models.py
+++ b/utils/test/testapi/opnfv_testapi/resources/result_models.py
@@ -54,6 +54,8 @@ class ResultCreateRequest(models.ModelBase):
build_tag=None,
scenario=None,
criteria=None,
+ user=None,
+ public="true",
trust_indicator=None):
self.pod_name = pod_name
self.project_name = project_name
@@ -66,6 +68,8 @@ class ResultCreateRequest(models.ModelBase):
self.build_tag = build_tag
self.scenario = scenario
self.criteria = criteria
+ self.user = user
+ self.public = public
self.trust_indicator = trust_indicator if trust_indicator else TI(0)
@@ -89,7 +93,7 @@ class TestResult(models.ModelBase):
pod_name=None, installer=None, version=None,
start_date=None, stop_date=None, details=None,
build_tag=None, scenario=None, criteria=None,
- trust_indicator=None):
+ user=None, public="true", trust_indicator=None):
self._id = _id
self.case_name = case_name
self.project_name = project_name
@@ -102,6 +106,8 @@ class TestResult(models.ModelBase):
self.build_tag = build_tag
self.scenario = scenario
self.criteria = criteria
+ self.user = user
+ self.public = public
self.trust_indicator = trust_indicator
@staticmethod
diff --git a/utils/test/testapi/opnfv_testapi/resources/scenario_handlers.py b/utils/test/testapi/opnfv_testapi/resources/scenario_handlers.py
index 5d420a56e..e9c19a7a4 100644
--- a/utils/test/testapi/opnfv_testapi/resources/scenario_handlers.py
+++ b/utils/test/testapi/opnfv_testapi/resources/scenario_handlers.py
@@ -15,6 +15,24 @@ class GenericScenarioHandler(handlers.GenericApiHandler):
self.table = self.db_scenarios
self.table_cls = models.Scenario
+ def set_query(self, locators):
+ query = dict()
+ elem_query = dict()
+ for k, v in locators.iteritems():
+ if k == 'scenario':
+ query['name'] = v
+ elif k == 'installer':
+ elem_query["installer"] = v
+ elif k == 'version':
+ elem_query["versions.version"] = v
+ elif k == 'project':
+ elem_query["versions.projects.project"] = v
+ else:
+ query[k] = v
+ if elem_query:
+ query['installers'] = {'$elemMatch': elem_query}
+ return query
+
class ScenariosCLHandler(GenericScenarioHandler):
@swagger.operation(nickname="queryScenarios")
@@ -96,10 +114,10 @@ class ScenarioGURHandler(GenericScenarioHandler):
self._get_one(query={'name': name})
pass
- @swagger.operation(nickname="updateScenarioByName")
+ @swagger.operation(nickname="updateScenarioName")
def put(self, name):
"""
- @description: update a single scenario by name
+ @description: update scenario, only rename is supported currently
@param body: fields to be updated
@type body: L{ScenarioUpdateRequest}
@in body: body
@@ -119,164 +137,639 @@ class ScenarioGURHandler(GenericScenarioHandler):
@return 200: delete success
@raise 404: scenario not exist:
"""
-
self._delete(query={'name': name})
- def _update_query(self, keys, data):
- query = dict()
- if self._is_rename():
- new = self._term.get('name')
- if data.get('name') != new:
- query['name'] = new
- return query
+class ScenarioUpdater(object):
+ def __init__(self, data, body=None,
+ installer=None, version=None, project=None):
+ self.data = data
+ self.body = body
+ self.installer = installer
+ self.version = version
+ self.project = project
- def _update_requests(self, data):
+ def update(self, item, action):
updates = {
- ('name', 'update'): self._update_requests_rename,
- ('installer', 'add'): self._update_requests_add_installer,
- ('installer', 'delete'): self._update_requests_delete_installer,
- ('version', 'add'): self._update_requests_add_version,
- ('version', 'delete'): self._update_requests_delete_version,
- ('owner', 'update'): self._update_requests_change_owner,
- ('project', 'add'): self._update_requests_add_project,
- ('project', 'delete'): self._update_requests_delete_project,
- ('customs', 'add'): self._update_requests_add_customs,
+ ('scores', 'post'): self._update_requests_add_score,
+ ('trust_indicators', 'post'): self._update_requests_add_ti,
+ ('customs', 'post'): self._update_requests_add_customs,
+ ('customs', 'put'): self._update_requests_update_customs,
('customs', 'delete'): self._update_requests_delete_customs,
- ('score', 'add'): self._update_requests_add_score,
- ('trust_indicator', 'add'): self._update_requests_add_ti,
+ ('projects', 'post'): self._update_requests_add_projects,
+ ('projects', 'put'): self._update_requests_update_projects,
+ ('projects', 'delete'): self._update_requests_delete_projects,
+ ('owner', 'put'): self._update_requests_change_owner,
+ ('versions', 'post'): self._update_requests_add_versions,
+ ('versions', 'put'): self._update_requests_update_versions,
+ ('versions', 'delete'): self._update_requests_delete_versions,
+ ('installers', 'post'): self._update_requests_add_installers,
+ ('installers', 'put'): self._update_requests_update_installers,
+ ('installers', 'delete'): self._update_requests_delete_installers,
}
+ updates[(item, action)](self.data)
- updates[(self._field, self._op)](data)
-
- return data.format()
+ return self.data.format()
- def _iter_installers(xstep):
+ def iter_installers(xstep):
@functools.wraps(xstep)
def magic(self, data):
[xstep(self, installer)
for installer in self._filter_installers(data.installers)]
return magic
- def _iter_versions(xstep):
+ def iter_versions(xstep):
@functools.wraps(xstep)
def magic(self, installer):
[xstep(self, version)
for version in (self._filter_versions(installer.versions))]
return magic
- def _iter_projects(xstep):
+ def iter_projects(xstep):
@functools.wraps(xstep)
def magic(self, version):
[xstep(self, project)
for project in (self._filter_projects(version.projects))]
return magic
- def _update_requests_rename(self, data):
- data.name = self._term.get('name')
- if not data.name:
- raises.BadRequest(message.missing('name'))
-
- def _update_requests_add_installer(self, data):
- data.installers.append(models.ScenarioInstaller.from_dict(self._term))
-
- def _update_requests_delete_installer(self, data):
- data.installers = self._remove_installers(data.installers)
-
- @_iter_installers
- def _update_requests_add_version(self, installer):
- installer.versions.append(models.ScenarioVersion.from_dict(self._term))
-
- @_iter_installers
- def _update_requests_delete_version(self, installer):
- installer.versions = self._remove_versions(installer.versions)
-
- @_iter_installers
- @_iter_versions
- def _update_requests_change_owner(self, version):
- version.owner = self._term.get('owner')
-
- @_iter_installers
- @_iter_versions
- def _update_requests_add_project(self, version):
- version.projects.append(models.ScenarioProject.from_dict(self._term))
+ @iter_installers
+ @iter_versions
+ @iter_projects
+ def _update_requests_add_score(self, project):
+ project.scores.append(
+ models.ScenarioScore.from_dict(self.body))
- @_iter_installers
- @_iter_versions
- def _update_requests_delete_project(self, version):
- version.projects = self._remove_projects(version.projects)
+ @iter_installers
+ @iter_versions
+ @iter_projects
+ def _update_requests_add_ti(self, project):
+ project.trust_indicators.append(
+ models.ScenarioTI.from_dict(self.body))
- @_iter_installers
- @_iter_versions
- @_iter_projects
+ @iter_installers
+ @iter_versions
+ @iter_projects
def _update_requests_add_customs(self, project):
- project.customs = list(set(project.customs + self._term))
+ project.customs = list(set(project.customs + self.body))
- @_iter_installers
- @_iter_versions
- @_iter_projects
+ @iter_installers
+ @iter_versions
+ @iter_projects
+ def _update_requests_update_customs(self, project):
+ project.customs = list(set(self.body))
+
+ @iter_installers
+ @iter_versions
+ @iter_projects
def _update_requests_delete_customs(self, project):
project.customs = filter(
- lambda f: f not in self._term,
+ lambda f: f not in self.body,
project.customs)
- @_iter_installers
- @_iter_versions
- @_iter_projects
- def _update_requests_add_score(self, project):
- project.scores.append(
- models.ScenarioScore.from_dict(self._term))
+ @iter_installers
+ @iter_versions
+ def _update_requests_add_projects(self, version):
+ version.projects = self._update_with_body(models.ScenarioProject,
+ 'project',
+ version.projects)
+
+ @iter_installers
+ @iter_versions
+ def _update_requests_update_projects(self, version):
+ version.projects = self._update_with_body(models.ScenarioProject,
+ 'project',
+ list())
+
+ @iter_installers
+ @iter_versions
+ def _update_requests_delete_projects(self, version):
+ version.projects = self._remove_projects(version.projects)
- @_iter_installers
- @_iter_versions
- @_iter_projects
- def _update_requests_add_ti(self, project):
- project.trust_indicators.append(
- models.ScenarioTI.from_dict(self._term))
+ @iter_installers
+ @iter_versions
+ def _update_requests_change_owner(self, version):
+ version.owner = self.body.get('owner')
+
+ @iter_installers
+ def _update_requests_add_versions(self, installer):
+ installer.versions = self._update_with_body(models.ScenarioVersion,
+ 'version',
+ installer.versions)
+
+ @iter_installers
+ def _update_requests_update_versions(self, installer):
+ installer.versions = self._update_with_body(models.ScenarioVersion,
+ 'version',
+ list())
+
+ @iter_installers
+ def _update_requests_delete_versions(self, installer):
+ installer.versions = self._remove_versions(installer.versions)
+
+ def _update_requests_add_installers(self, scenario):
+ scenario.installers = self._update_with_body(models.ScenarioInstaller,
+ 'installer',
+ scenario.installers)
+
+ def _update_requests_update_installers(self, scenario):
+ scenario.installers = self._update_with_body(models.ScenarioInstaller,
+ 'installer',
+ list())
+
+ def _update_requests_delete_installers(self, scenario):
+ scenario.installers = self._remove_installers(scenario.installers)
+
+ def _update_with_body(self, clazz, field, withs):
+ exists = list()
+ malformat = list()
+ for new in self.body:
+ try:
+ format_new = clazz.from_dict_with_raise(new)
+ new_name = getattr(format_new, field)
+ if not any(getattr(o, field) == new_name for o in withs):
+ withs.append(format_new)
+ else:
+ exists.append(new_name)
+ except Exception as error:
+ malformat.append(error.message)
+ if malformat:
+ raises.BadRequest(message.bad_format(malformat))
+ elif exists:
+ raises.Conflict(message.exist('{}s'.format(field), exists))
+ return withs
- def _is_rename(self):
- return self._field == 'name' and self._op == 'update'
+ def _filter_installers(self, installers):
+ return self._filter('installer', installers)
def _remove_installers(self, installers):
return self._remove('installer', installers)
- def _filter_installers(self, installers):
- return self._filter('installer', installers)
+ def _filter_versions(self, versions):
+ return self._filter('version', versions)
def _remove_versions(self, versions):
return self._remove('version', versions)
- def _filter_versions(self, versions):
- return self._filter('version', versions)
+ def _filter_projects(self, projects):
+ return self._filter('project', projects)
def _remove_projects(self, projects):
return self._remove('project', projects)
- def _filter_projects(self, projects):
- return self._filter('project', projects)
+ def _filter(self, item, items):
+ return filter(
+ lambda f: getattr(f, item) == getattr(self, item),
+ items)
def _remove(self, field, fields):
return filter(
- lambda f: getattr(f, field) != self._locate.get(field),
+ lambda f: getattr(f, field) not in self.body,
fields)
- def _filter(self, field, fields):
- return filter(
- lambda f: getattr(f, field) == self._locate.get(field),
- fields)
- @property
- def _field(self):
- return self.json_args.get('field')
+class GenericScenarioUpdateHandler(GenericScenarioHandler):
+ def __init__(self, application, request, **kwargs):
+ super(GenericScenarioUpdateHandler, self).__init__(application,
+ request,
+ **kwargs)
+ self.installer = None
+ self.version = None
+ self.project = None
+ self.item = None
+ self.action = None
+
+ def do_update(self, item, action, locators):
+ self.item = item
+ self.action = action
+ for k, v in locators.iteritems():
+ if not v:
+ v = self.get_query_argument(k)
+ setattr(self, k, v)
+ locators[k] = v
+ self.pure_update(query=self.set_query(locators=locators))
+
+ def _update_requests(self, data):
+ return ScenarioUpdater(data,
+ self.json_args,
+ self.installer,
+ self.version,
+ self.project).update(self.item, self.action)
+
+
+class ScenarioScoresHandler(GenericScenarioUpdateHandler):
+ @swagger.operation(nickname="addScoreRecord")
+ def post(self, scenario):
+ """
+ @description: add a new score record
+ @notes: add a new score record to a project
+ POST /api/v1/scenarios/<scenario_name>/scores? \
+ installer=<installer_name>& \
+ version=<version_name>& \
+ project=<project_name>
+ @param body: score to be added
+ @type body: L{ScenarioScore}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: True
+ @param project: project name
+ @type project: L{string}
+ @in project: query
+ @required project: True
+ @return 200: score is created.
+ @raise 404: scenario/installer/version/project not existed
+ """
+ self.do_update('scores',
+ 'post',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None,
+ 'project': None})
+
+
+class ScenarioTIsHandler(GenericScenarioUpdateHandler):
+ @swagger.operation(nickname="addTrustIndicatorRecord")
+ def post(self, scenario):
+ """
+ @description: add a new trust indicator record
+ @notes: add a new trust indicator record to a project
+ POST /api/v1/scenarios/<scenario_name>/trust_indicators? \
+ installer=<installer_name>& \
+ version=<version_name>& \
+ project=<project_name>
+ @param body: trust indicator to be added
+ @type body: L{ScenarioTI}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: True
+ @param project: project name
+ @type project: L{string}
+ @in project: query
+ @required project: True
+ @return 200: trust indicator is added.
+ @raise 404: scenario/installer/version/project not existed
+ """
+ self.do_update('trust_indicators',
+ 'post',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None,
+ 'project': None})
+
+
+class ScenarioCustomsHandler(GenericScenarioUpdateHandler):
+ @swagger.operation(nickname="addCustomizedTestCases")
+ def post(self, scenario):
+ """
+ @description: add customized test cases
+ @notes: add several test cases to a project
+ POST /api/v1/scenarios/<scenario_name>/customs? \
+ installer=<installer_name>& \
+ version=<version_name>& \
+ project=<project_name>
+ @param body: test cases to be added
+ @type body: C{list} of L{string}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: True
+ @param project: project name
+ @type project: L{string}
+ @in project: query
+ @required project: True
+ @return 200: test cases are added.
+ @raise 404: scenario/installer/version/project not existed
+ """
+ self.do_update('customs',
+ 'post',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None,
+ 'project': None})
+
+ @swagger.operation(nickname="updateCustomizedTestCases")
+ def put(self, scenario):
+ """
+ @description: update customized test cases
+ @notes: substitute all the customized test cases
+ PUT /api/v1/scenarios/<scenario_name>/customs? \
+ installer=<installer_name>& \
+ version=<version_name>& \
+ project=<project_name>
+ @param body: new supported test cases
+ @type body: C{list} of L{string}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: True
+ @param project: project name
+ @type project: L{string}
+ @in project: query
+ @required project: True
+ @return 200: substitute test cases success.
+ @raise 404: scenario/installer/version/project not existed
+ """
+ self.do_update('customs',
+ 'put',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None,
+ 'project': None})
+
+ @swagger.operation(nickname="deleteCustomizedTestCases")
+ def delete(self, scenario):
+ """
+ @description: delete one or several customized test cases
+ @notes: delete one or some customized test cases
+ DELETE /api/v1/scenarios/<scenario_name>/customs? \
+ installer=<installer_name>& \
+ version=<version_name>& \
+ project=<project_name>
+ @param body: test case(s) to be deleted
+ @type body: C{list} of L{string}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: True
+ @param project: project name
+ @type project: L{string}
+ @in project: query
+ @required project: True
+ @return 200: delete test case(s) success.
+ @raise 404: scenario/installer/version/project not existed
+ """
+ self.do_update('customs',
+ 'delete',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None,
+ 'project': None})
- @property
- def _op(self):
- return self.json_args.get('op')
- @property
- def _locate(self):
- return self.json_args.get('locate')
+class ScenarioProjectsHandler(GenericScenarioUpdateHandler):
+ @swagger.operation(nickname="addProjectsUnderScenario")
+ def post(self, scenario):
+ """
+ @description: add projects to scenario
+ @notes: add one or multiple projects
+ POST /api/v1/scenarios/<scenario_name>/projects? \
+ installer=<installer_name>& \
+ version=<version_name>
+ @param body: projects to be added
+ @type body: C{list} of L{ScenarioProject}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: True
+ @return 200: projects are added.
+ @raise 400: bad schema
+ @raise 409: conflict, project already exists
+ @raise 404: scenario/installer/version not existed
+ """
+ self.do_update('projects',
+ 'post',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None})
+
+ @swagger.operation(nickname="updateScenarioProjects")
+ def put(self, scenario):
+ """
+ @description: replace all projects
+ @notes: substitute all projects, delete existed ones with new provides
+ PUT /api/v1/scenarios/<scenario_name>/projects? \
+ installer=<installer_name>& \
+ version=<version_name>
+ @param body: new projects
+ @type body: C{list} of L{ScenarioProject}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: True
+ @return 200: replace projects success.
+ @raise 400: bad schema
+ @raise 404: scenario/installer/version not existed
+ """
+ self.do_update('projects',
+ 'put',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None})
+
+ @swagger.operation(nickname="deleteProjectsUnderScenario")
+ def delete(self, scenario):
+ """
+ @description: delete one or multiple projects
+ @notes: delete one or multiple projects
+ DELETE /api/v1/scenarios/<scenario_name>/projects? \
+ installer=<installer_name>& \
+ version=<version_name>
+ @param body: projects(names) to be deleted
+ @type body: C{list} of L{string}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: True
+ @return 200: delete project(s) success.
+ @raise 404: scenario/installer/version not existed
+ """
+ self.do_update('projects',
+ 'delete',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None})
- @property
- def _term(self):
- return self.json_args.get('term')
+
+class ScenarioOwnerHandler(GenericScenarioUpdateHandler):
+ @swagger.operation(nickname="changeScenarioOwner")
+ def put(self, scenario):
+ """
+ @description: change scenario owner
+ @notes: substitute all projects, delete existed ones with new provides
+ PUT /api/v1/scenarios/<scenario_name>/owner? \
+ installer=<installer_name>& \
+ version=<version_name>
+ @param body: new owner
+ @type body: L{ScenarioChangeOwnerRequest}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: True
+ @return 200: change owner success.
+ @raise 404: scenario/installer/version not existed
+ """
+ self.do_update('owner',
+ 'put',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None})
+
+
+class ScenarioVersionsHandler(GenericScenarioUpdateHandler):
+ @swagger.operation(nickname="addVersionsUnderScenario")
+ def post(self, scenario):
+ """
+ @description: add versions to scenario
+ @notes: add one or multiple versions
+ POST /api/v1/scenarios/<scenario_name>/versions? \
+ installer=<installer_name>
+ @param body: versions to be added
+ @type body: C{list} of L{ScenarioVersion}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @return 200: versions are added.
+ @raise 400: bad schema
+ @raise 409: conflict, version already exists
+ @raise 404: scenario/installer not exist
+ """
+ self.do_update('versions',
+ 'post',
+ locators={'scenario': scenario,
+ 'installer': None})
+
+ @swagger.operation(nickname="updateVersionsUnderScenario")
+ def put(self, scenario):
+ """
+ @description: replace all versions
+ @notes: substitute all versions as a totality
+ PUT /api/v1/scenarios/<scenario_name>/versions? \
+ installer=<installer_name>
+ @param body: new versions
+ @type body: C{list} of L{ScenarioVersion}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @return 200: replace versions success.
+ @raise 400: bad schema
+ @raise 404: scenario/installer not exist
+ """
+ self.do_update('versions',
+ 'put',
+ locators={'scenario': scenario,
+ 'installer': None})
+
+ @swagger.operation(nickname="deleteVersionsUnderScenario")
+ def delete(self, scenario):
+ """
+ @description: delete one or multiple versions
+ @notes: delete one or multiple versions
+ DELETE /api/v1/scenarios/<scenario_name>/versions? \
+ installer=<installer_name>
+ @param body: versions(names) to be deleted
+ @type body: C{list} of L{string}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @return 200: delete versions success.
+ @raise 404: scenario/installer not exist
+ """
+ self.do_update('versions',
+ 'delete',
+ locators={'scenario': scenario,
+ 'installer': None})
+
+
+class ScenarioInstallersHandler(GenericScenarioUpdateHandler):
+ @swagger.operation(nickname="addInstallersUnderScenario")
+ def post(self, scenario):
+ """
+ @description: add installers to scenario
+ @notes: add one or multiple installers
+ POST /api/v1/scenarios/<scenario_name>/installers
+ @param body: installers to be added
+ @type body: C{list} of L{ScenarioInstaller}
+ @in body: body
+ @return 200: installers are added.
+ @raise 400: bad schema
+ @raise 409: conflict, installer already exists
+ @raise 404: scenario not exist
+ """
+ self.do_update('installers',
+ 'post',
+ locators={'scenario': scenario})
+
+ @swagger.operation(nickname="updateInstallersUnderScenario")
+ def put(self, scenario):
+ """
+ @description: replace all installers
+ @notes: substitute all installers as a totality
+ PUT /api/v1/scenarios/<scenario_name>/installers
+ @param body: new installers
+ @type body: C{list} of L{ScenarioInstaller}
+ @in body: body
+ @return 200: replace versions success.
+ @raise 400: bad schema
+ @raise 404: scenario/installer not exist
+ """
+ self.do_update('installers',
+ 'put',
+ locators={'scenario': scenario})
+
+ @swagger.operation(nickname="deleteInstallersUnderScenario")
+ def delete(self, scenario):
+ """
+ @description: delete one or multiple installers
+ @notes: delete one or multiple installers
+ DELETE /api/v1/scenarios/<scenario_name>/installers
+ @param body: installers(names) to be deleted
+ @type body: C{list} of L{string}
+ @in body: body
+ @return 200: delete versions success.
+ @raise 404: scenario/installer not exist
+ """
+ self.do_update('installers',
+ 'delete',
+ locators={'scenario': scenario})
diff --git a/utils/test/testapi/opnfv_testapi/resources/scenario_models.py b/utils/test/testapi/opnfv_testapi/resources/scenario_models.py
index 467cff241..d950ed1d7 100644
--- a/utils/test/testapi/opnfv_testapi/resources/scenario_models.py
+++ b/utils/test/testapi/opnfv_testapi/resources/scenario_models.py
@@ -16,6 +16,13 @@ class ScenarioTI(models.ModelBase):
self.date = date
self.status = status
+ def __eq__(self, other):
+ return (self.date == other.date and
+ self.status == other.status)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
@swagger.model()
class ScenarioScore(models.ModelBase):
@@ -23,6 +30,13 @@ class ScenarioScore(models.ModelBase):
self.date = date
self.score = score
+ def __eq__(self, other):
+ return (self.date == other.date and
+ self.score == other.score)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
@swagger.model()
class ScenarioProject(models.ModelBase):
@@ -50,10 +64,10 @@ class ScenarioProject(models.ModelBase):
'trust_indicators': ScenarioTI}
def __eq__(self, other):
- return [self.project == other.project and
+ return (self.project == other.project and
self._customs_eq(other) and
self._scores_eq(other) and
- self._ti_eq(other)]
+ self._ti_eq(other))
def __ne__(self, other):
return not self.__eq__(other)
@@ -62,10 +76,10 @@ class ScenarioProject(models.ModelBase):
return set(self.customs) == set(other.customs)
def _scores_eq(self, other):
- return set(self.scores) == set(other.scores)
+ return self.scores == other.scores
def _ti_eq(self, other):
- return set(self.trust_indicators) == set(other.trust_indicators)
+ return self.trust_indicators == other.trust_indicators
@swagger.model()
@@ -74,7 +88,8 @@ class ScenarioVersion(models.ModelBase):
@property projects:
@ptype projects: C{list} of L{ScenarioProject}
"""
- def __init__(self, version=None, projects=None):
+ def __init__(self, owner=None, version=None, projects=None):
+ self.owner = owner
self.version = version
self.projects = list_default(projects)
@@ -83,7 +98,9 @@ class ScenarioVersion(models.ModelBase):
return {'projects': ScenarioProject}
def __eq__(self, other):
- return [self.version == other.version and self._projects_eq(other)]
+ return (self.version == other.version and
+ self.owner == other.owner and
+ self._projects_eq(other))
def __ne__(self, other):
return not self.__eq__(other)
@@ -113,7 +130,7 @@ class ScenarioInstaller(models.ModelBase):
return {'versions': ScenarioVersion}
def __eq__(self, other):
- return [self.installer == other.installer and self._versions_eq(other)]
+ return (self.installer == other.installer and self._versions_eq(other))
def __ne__(self, other):
return not self.__eq__(other)
@@ -144,18 +161,15 @@ class ScenarioCreateRequest(models.ModelBase):
@swagger.model()
+class ScenarioChangeOwnerRequest(models.ModelBase):
+ def __init__(self, owner=None):
+ self.owner = owner
+
+
+@swagger.model()
class ScenarioUpdateRequest(models.ModelBase):
- """
- @property field: update field
- @property op: add/delete/update
- @property locate: information used to locate the field
- @property term: new value
- """
- def __init__(self, field=None, op=None, locate=None, term=None):
- self.field = field
- self.op = op
- self.locate = dict_default(locate)
- self.term = dict_default(term)
+ def __init__(self, name=None):
+ self.name = name
@swagger.model()
@@ -178,7 +192,7 @@ class Scenario(models.ModelBase):
return not self.__eq__(other)
def __eq__(self, other):
- return [self.name == other.name and self._installers_eq(other)]
+ return (self.name == other.name and self._installers_eq(other))
def _installers_eq(self, other):
for s_install in self.installers: