From 5362ddab5a739f5198d3e7918b6059c27a4eac09 Mon Sep 17 00:00:00 2001 From: SerenaFeng Date: Thu, 26 May 2016 20:32:02 +0800 Subject: swagger-ize project-apis of testAPI rename pod_handler.py to pod_handlers.py JIRA: FUNCTEST-264 Change-Id: I8699999776bdb238f680a128b83cea0a098534c5 Signed-off-by: SerenaFeng --- .../result_collection_api/resources/handlers.py | 204 +++++---------------- .../result_collection_api/resources/pod_handler.py | 89 --------- .../resources/pod_handlers.py | 75 ++++++++ .../result_collection_api/resources/pod_models.py | 6 +- .../resources/project_handlers.py | 124 +++++++++++++ .../resources/project_models.py | 34 +++- .../result_collection_api/result_collection_api.py | 9 +- .../result_collection_api/tests/unit/test_base.py | 9 +- .../tests/unit/test_project.py | 12 ++ 9 files changed, 294 insertions(+), 268 deletions(-) delete mode 100644 utils/test/result_collection_api/resources/pod_handler.py create mode 100644 utils/test/result_collection_api/resources/pod_handlers.py create mode 100644 utils/test/result_collection_api/resources/project_handlers.py diff --git a/utils/test/result_collection_api/resources/handlers.py b/utils/test/result_collection_api/resources/handlers.py index afee1cd46..3f9d8422c 100644 --- a/utils/test/result_collection_api/resources/handlers.py +++ b/utils/test/result_collection_api/resources/handlers.py @@ -25,7 +25,6 @@ from tornado import gen from models import CreateResponse from resources.result_models import TestResult from resources.testcase_models import Testcase -from resources.project_models import Project from common.constants import DEFAULT_REPRESENTATION, HTTP_BAD_REQUEST, \ HTTP_NOT_FOUND, HTTP_FORBIDDEN from common.config import prepare_put_request @@ -39,15 +38,12 @@ def format_data(data, cls): class GenericApiHandler(RequestHandler): - """ - The purpose of this class is to take benefit of inheritance and prepare - a set of common functions for - the handlers - """ - - def initialize(self): - """ Prepares the database for the entire class """ + 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 def prepare(self): if self.request.method != "GET" and self.request.method != "DELETE": @@ -60,8 +56,6 @@ class GenericApiHandler(RequestHandler): raise HTTPError(HTTP_BAD_REQUEST, "Bad Json format [{}]". format(error)) - else: - self.json_args = None def finish_request(self, json_object=None): if json_object: @@ -75,32 +69,57 @@ class GenericApiHandler(RequestHandler): @asynchronous @gen.coroutine - def _create(self, table, data, mark): + def _create(self, error): + if self.json_args is None: + raise HTTPError(HTTP_BAD_REQUEST, 'no body') + + data = self.table_cls.from_dict(self.json_args) + name = data.name + if name is None or name == '': + raise HTTPError(HTTP_BAD_REQUEST, + '{} name missing'.format(self.table[:-1])) + + exist_data = yield self._eval_db(self.table, 'find_one', + {"name": name}) + if exist_data is not None: + raise HTTPError(HTTP_FORBIDDEN, + error.format(name, self.table[:-1])) data.creation_date = datetime.now() - _id = yield self._eval_db(table, 'insert', data.format()) - if mark is None: - mark = _id - self.finish_request(self._create_response(mark)) + yield self._eval_db(self.table, 'insert', data.format()) + self.finish_request(self._create_response(name)) @asynchronous @gen.coroutine - def _list(self, table, format_cls, query=None): + def _list(self, query=None): if query is None: query = {} res = [] - cursor = self._eval_db(table, 'find', query) + cursor = self._eval_db(self.table, 'find', query) while (yield cursor.fetch_next): - res.append(format_data(cursor.next_object(), format_cls)) - self.finish_request({table: res}) + res.append(format_data(cursor.next_object(), self.table_cls)) + self.finish_request({self.table: res}) + + @asynchronous + @gen.coroutine + def _get_one(self, query): + data = yield self._eval_db(self.table, 'find_one', query) + if data is None: + raise HTTPError(HTTP_NOT_FOUND, + "[{}] not exist in table [{}]" + .format(query, self.table)) + self.finish_request(format_data(data, self.table_cls)) @asynchronous @gen.coroutine - def _get_one(self, table, format_cls, query): - data = yield self._eval_db(table, 'find_one', query) + def _delete(self, query): + data = yield self._eval_db(self.table, 'find_one', query) if data is None: raise HTTPError(HTTP_NOT_FOUND, - "{} Not Exist".format(query)) - self.finish_request(format_data(data, format_cls)) + "[{}] not exit in table [{}]" + .format(query, self.table)) + + yield self._eval_db(self.table, 'remove', query) + self.finish_request() def _eval_db(self, table, method, param): return eval('self.db.%s.%s(param)' % (table, method)) @@ -112,143 +131,6 @@ class VersionHandler(GenericApiHandler): self.finish_request([{'v1': 'basics'}]) -class ProjectHandler(GenericApiHandler): - """ - TestProjectHandler Class - Handle the requests about the Test projects - HTTP Methdods : - - GET : Get all test projects and details about a specific one - - POST : Add a test project - - PUT : Edit test projects information (name and/or description) - - DELETE : Remove a test project - """ - - def initialize(self): - """ Prepares the database for the entire class """ - super(ProjectHandler, self).initialize() - - @asynchronous - @gen.coroutine - def get(self, project_name=None): - """ - Get Project(s) info - :param project_name: - """ - - query = dict() - - if project_name is not None: - query["name"] = project_name - answer = yield self.db.projects.find_one(query) - if answer is None: - raise HTTPError(HTTP_NOT_FOUND, - "{} Not Exist".format(project_name)) - else: - answer = format_data(answer, Project) - else: - res = [] - cursor = self.db.projects.find(query) - while (yield cursor.fetch_next): - res.append(format_data(cursor.next_object(), Project)) - answer = {'projects': res} - - self.finish_request(answer) - - @asynchronous - @gen.coroutine - def post(self): - """ Create a test project""" - - if self.json_args is None: - raise HTTPError(HTTP_BAD_REQUEST) - - query = {"name": self.json_args.get("name")} - - # check for name in db - the_project = yield self.db.projects.find_one(query) - if the_project is not None: - raise HTTPError(HTTP_FORBIDDEN, - "{} already exists as a project".format( - self.json_args.get("name"))) - - project = Project.from_dict(self.json_args) - project.creation_date = datetime.now() - - yield self.db.projects.insert(project.format()) - self.finish_request(self._create_response(project.name)) - - @asynchronous - @gen.coroutine - def put(self, project_name): - """ Updates the name and description of a test project""" - - if self.json_args is None: - raise HTTPError(HTTP_BAD_REQUEST) - - query = {'name': project_name} - from_project = yield self.db.projects.find_one(query) - if from_project is None: - raise HTTPError(HTTP_NOT_FOUND, - "{} could not be found".format(project_name)) - - project = Project.from_dict(from_project) - new_name = self.json_args.get("name") - new_description = self.json_args.get("description") - - # check for payload name parameter in db - # avoid a request if the project name has not changed in the payload - if new_name != project.name: - to_project = yield self.db.projects.find_one( - {"name": new_name}) - if to_project is not None: - raise HTTPError(HTTP_FORBIDDEN, - "{} already exists as a project" - .format(new_name)) - - # new dict for changes - request = dict() - request = prepare_put_request(request, - "name", - new_name, - project.name) - request = prepare_put_request(request, - "description", - new_description, - project.description) - - """ raise exception if there isn't a change """ - if not request: - raise HTTPError(HTTP_FORBIDDEN, "Nothing to update") - - """ we merge the whole document """ - edit_request = project.format() - edit_request.update(request) - - """ Updating the DB """ - yield self.db.projects.update({'name': project_name}, edit_request) - new_project = yield self.db.projects.find_one({"_id": project._id}) - - self.finish_request(format_data(new_project, Project)) - - @asynchronous - @gen.coroutine - def delete(self, project_name): - """ Remove a test project""" - query = {'name': project_name} - - # check for an existing project to be deleted - project = yield self.db.projects.find_one(query) - if project is None: - raise HTTPError(HTTP_NOT_FOUND, - "{} could not be found as a project to be deleted" - .format(project_name)) - - # just delete it, or maybe save it elsewhere in a future - yield self.db.projects.remove(query) - - self.finish_request() - - class TestcaseHandler(GenericApiHandler): """ TestCasesHandler Class diff --git a/utils/test/result_collection_api/resources/pod_handler.py b/utils/test/result_collection_api/resources/pod_handler.py deleted file mode 100644 index 718996784..000000000 --- a/utils/test/result_collection_api/resources/pod_handler.py +++ /dev/null @@ -1,89 +0,0 @@ -from tornado import gen -from tornado.web import HTTPError, asynchronous - -from tornado_swagger_ui.tornado_swagger import swagger -from handlers import GenericApiHandler -from common.constants import HTTP_BAD_REQUEST, HTTP_FORBIDDEN -from pod_models import Pod - - -class PodCLHandler(GenericApiHandler): - def initialize(self): - super(PodCLHandler, self).initialize() - - @swagger.operation(nickname='list-all') - def get(self): - """ - @description: list all PODs - @return 200: list all pods, empty list is no pod exist - @rtype: L{Pods} - """ - self._list('pods', Pod) - - # @asynchronous - @gen.coroutine - @swagger.operation(nickname='create') - def post(self): - """ - @description: Create a POD - @param body: pod information to be created - @type body: L{PodCreateRequest} - @in body: body - @return 200: pod is created. - @rtype: L{Pod} - @raise 403: already exists as a pod - @raise 400: bad request - """ - if self.json_args is None: - raise HTTPError(HTTP_BAD_REQUEST, 'no payload') - - pod = Pod.from_dict(self.json_args) - name = pod.name - if name is None or name == '': - raise HTTPError(HTTP_BAD_REQUEST, 'pod name missing') - - the_pod = yield self.db.pods.find_one({'name': name}) - if the_pod is not None: - raise HTTPError(HTTP_FORBIDDEN, - "{} already exists as a pod".format( - self.json_args.get("name"))) - self._create('pods', pod, name) - - -class PodGURHandler(GenericApiHandler): - def initialize(self): - super(PodGURHandler, self).initialize() - - @swagger.operation(nickname='get-one') - def get(self, pod_name=None): - """ - @description: Get a single pod by pod_name - @return 200: pod exist - @raise 404: pod not exist - @rtype: L{Pod} - """ - query = dict() - query["name"] = pod_name - self._get_one('pods', Pod, query) - - @asynchronous - @gen.coroutine - def delete(self, pod_name): - """ Remove a POD - - # check for an existing pod to be deleted - mongo_dict = yield self.db.pods.find_one( - {'name': pod_name}) - pod = TestProject.pod(mongo_dict) - if pod is None: - raise HTTPError(HTTP_NOT_FOUND, - "{} could not be found as a pod to be deleted" - .format(pod_name)) - - # just delete it, or maybe save it elsewhere in a future - res = yield self.db.projects.remove( - {'name': pod_name}) - - self.finish_request(answer) - """ - pass diff --git a/utils/test/result_collection_api/resources/pod_handlers.py b/utils/test/result_collection_api/resources/pod_handlers.py new file mode 100644 index 000000000..590ae5baf --- /dev/null +++ b/utils/test/result_collection_api/resources/pod_handlers.py @@ -0,0 +1,75 @@ +from tornado import gen +from tornado.web import asynchronous + +from tornado_swagger_ui.tornado_swagger import swagger +from handlers import GenericApiHandler +from pod_models import Pod + + +class GenericPodHandler(GenericApiHandler): + def __init__(self, application, request, **kwargs): + super(GenericPodHandler, self).__init__(application, request, **kwargs) + self.table = 'pods' + self.table_cls = Pod + + +class PodCLHandler(GenericPodHandler): + @swagger.operation(nickname='list-all') + def get(self): + """ + @description: list all pods + @return 200: list all pods, empty list is no pod exist + @rtype: L{Pods} + """ + self._list() + + @gen.coroutine + @swagger.operation(nickname='create') + def post(self): + """ + @description: create a pod + @param body: pod to be created + @type body: L{PodCreateRequest} + @in body: body + @rtype: L{Pod} + @return 200: pod is created. + @raise 403: pod already exists + @raise 400: post without body + """ + self._create('{} already exists as a {}') + + +class PodGURHandler(GenericPodHandler): + @swagger.operation(nickname='get-one') + def get(self, pod_name): + """ + @description: get a single pod by pod_name + @rtype: L{Pod} + @return 200: pod exist + @raise 404: pod not exist + """ + query = dict() + query['name'] = pod_name + self._get_one(query) + + @asynchronous + @gen.coroutine + def delete(self, pod_name): + """ Remove a POD + + # check for an existing pod to be deleted + mongo_dict = yield self.db.pods.find_one( + {'name': pod_name}) + pod = TestProject.pod(mongo_dict) + if pod is None: + raise HTTPError(HTTP_NOT_FOUND, + "{} could not be found as a pod to be deleted" + .format(pod_name)) + + # just delete it, or maybe save it elsewhere in a future + res = yield self.db.projects.remove( + {'name': pod_name}) + + self.finish_request(answer) + """ + pass diff --git a/utils/test/result_collection_api/resources/pod_models.py b/utils/test/result_collection_api/resources/pod_models.py index fcb4ddbbd..cc98c90ce 100644 --- a/utils/test/result_collection_api/resources/pod_models.py +++ b/utils/test/result_collection_api/resources/pod_models.py @@ -27,9 +27,9 @@ class PodCreateRequest(object): @swagger.model() class Pod(PodCreateRequest): - """ describes a POD platform """ - def __init__(self, name='', mode='', details='', role="", - _id='', create_date=''): + def __init__(self, + name='', mode='', details='', + role="", _id='', create_date=''): super(Pod, self).__init__(name, mode, details, role) self._id = _id self.creation_date = create_date diff --git a/utils/test/result_collection_api/resources/project_handlers.py b/utils/test/result_collection_api/resources/project_handlers.py new file mode 100644 index 000000000..69ce3b592 --- /dev/null +++ b/utils/test/result_collection_api/resources/project_handlers.py @@ -0,0 +1,124 @@ +from tornado import gen +from tornado.web import HTTPError, asynchronous + +from tornado_swagger_ui.tornado_swagger import swagger +from handlers import GenericApiHandler, prepare_put_request, format_data +from common.constants import HTTP_BAD_REQUEST, HTTP_FORBIDDEN, HTTP_NOT_FOUND +from project_models import Project + + +class GenericProjectHandler(GenericApiHandler): + def __init__(self, application, request, **kwargs): + super(GenericProjectHandler, self).__init__(application, + request, + **kwargs) + self.table = 'projects' + self.table_cls = Project + + +class ProjectCLHandler(GenericProjectHandler): + @swagger.operation(nickname="list-all") + def get(self): + """ + @description: list all projects + @return 200: return all projects, empty list is no project exist + @rtype: L{Projects} + """ + self._list() + + @swagger.operation(nickname="create") + def post(self): + """ + @description: create a project + @param body: project to be created + @type body: L{ProjectCreateRequest} + @in body: body + @rtype: L{Project} + @return 200: project is created. + @raise 403: project already exists + @raise 400: post without body + """ + self._create('{} already exists as a {}') + + +class ProjectGURHandler(GenericProjectHandler): + @swagger.operation(nickname='get-one') + def get(self, project_name): + """ + @description: get a single project by project_name + @rtype: L{Project} + @return 200: project exist + @raise 404: project not exist + """ + self._get_one({'name': project_name}) + + @asynchronous + @gen.coroutine + @swagger.operation(nickname="update") + def put(self, project_name): + """ + @description: update a single project by project_name + @param body: project to be updated + @type body: L{ProjectUpdateRequest} + @in body: body + @rtype: L{Project} + @return 200: update success + @raise 404: project not exist + @raise 403: new project name already exist or nothing to update + """ + if self.json_args is None: + raise HTTPError(HTTP_BAD_REQUEST) + + query = {'name': project_name} + from_project = yield self.db.projects.find_one(query) + if from_project is None: + raise HTTPError(HTTP_NOT_FOUND, + "{} could not be found".format(project_name)) + + project = Project.from_dict(from_project) + new_name = self.json_args.get("name") + new_description = self.json_args.get("description") + + # check for payload name parameter in db + # avoid a request if the project name has not changed in the payload + if new_name != project.name: + to_project = yield self.db.projects.find_one( + {"name": new_name}) + if to_project is not None: + raise HTTPError(HTTP_FORBIDDEN, + "{} already exists as a project" + .format(new_name)) + + # new dict for changes + request = dict() + request = prepare_put_request(request, + "name", + new_name, + project.name) + request = prepare_put_request(request, + "description", + new_description, + project.description) + + """ raise exception if there isn't a change """ + if not request: + raise HTTPError(HTTP_FORBIDDEN, "Nothing to update") + + """ we merge the whole document """ + edit_request = project.format() + edit_request.update(request) + + """ Updating the DB """ + yield self.db.projects.update(query, edit_request) + new_project = yield self.db.projects.find_one({"_id": project._id}) + + self.finish_request(format_data(new_project, Project)) + + @swagger.operation(nickname='delete') + def delete(self, project_name): + """ + @description: delete a project by project_name + @return 200: delete success + @raise 404: project not exist + """ + self._delete({'name': project_name}) diff --git a/utils/test/result_collection_api/resources/project_models.py b/utils/test/result_collection_api/resources/project_models.py index 895fc3e5e..a8f830932 100644 --- a/utils/test/result_collection_api/resources/project_models.py +++ b/utils/test/result_collection_api/resources/project_models.py @@ -1,7 +1,23 @@ +from tornado_swagger_ui.tornado_swagger import swagger + __author__ = '__serena__' +@swagger.model() class ProjectCreateRequest(object): + def __init__(self, name, description=''): + self.name = name + self.description = description + + def format(self): + return { + "name": self.name, + "description": self.description, + } + + +@swagger.model() +class ProjectUpdateRequest(object): def __init__(self, name='', description=''): self.name = name self.description = description @@ -13,14 +29,14 @@ class ProjectCreateRequest(object): } +@swagger.model() class Project: - """ Describes a test project""" - - def __init__(self): - self._id = None - self.name = None - self.description = None - self.creation_date = None + def __init__(self, + name=None, _id=None, description=None, create_date=None): + self._id = _id + self.name = name + self.description = description + self.creation_date = create_date @staticmethod def from_dict(res_dict): @@ -52,7 +68,11 @@ class Project: } +@swagger.model() class Projects(object): + """ + @ptype projects: C{list} of L{Project} + """ def __init__(self, projects=list()): self.projects = projects diff --git a/utils/test/result_collection_api/result_collection_api.py b/utils/test/result_collection_api/result_collection_api.py index afd66f3fc..344e0d7b0 100644 --- a/utils/test/result_collection_api/result_collection_api.py +++ b/utils/test/result_collection_api/result_collection_api.py @@ -35,8 +35,9 @@ import tornado.ioloop import motor from resources.handlers import VersionHandler, \ - ProjectHandler, TestcaseHandler, TestResultsHandler, DashboardHandler -from resources.pod_handler import PodCLHandler, PodGURHandler + TestcaseHandler, TestResultsHandler, DashboardHandler +from resources.pod_handlers import PodCLHandler, PodGURHandler +from resources.project_handlers import ProjectCLHandler, ProjectGURHandler from common.config import APIConfig from tornado_swagger_ui.tornado_swagger import swagger @@ -67,8 +68,8 @@ def make_app(): # few examples: # GET /projects # GET /projects/yardstick - (r"/api/v1/projects", ProjectHandler), - (r"/api/v1/projects/([^/]+)", ProjectHandler), + (r"/api/v1/projects", ProjectCLHandler), + (r"/api/v1/projects/([^/]+)", ProjectGURHandler), # few examples # GET /projects/qtip/cases => Get cases for qtip diff --git a/utils/test/result_collection_api/tests/unit/test_base.py b/utils/test/result_collection_api/tests/unit/test_base.py index 16ed07c9c..44e42b797 100644 --- a/utils/test/result_collection_api/tests/unit/test_base.py +++ b/utils/test/result_collection_api/tests/unit/test_base.py @@ -2,9 +2,10 @@ import json from tornado.web import Application from tornado.testing import AsyncHTTPTestCase -from resources.pod_handler import PodCLHandler, PodGURHandler +from resources.pod_handlers import PodCLHandler, PodGURHandler +from resources.project_handlers import ProjectCLHandler, ProjectGURHandler from resources.handlers import VersionHandler, \ - ProjectHandler, TestcaseHandler, TestResultsHandler, DashboardHandler + TestcaseHandler, TestResultsHandler, DashboardHandler from resources.models import CreateResponse import fake_pymongo @@ -29,8 +30,8 @@ class TestBase(AsyncHTTPTestCase): (r"/versions", VersionHandler), (r"/api/v1/pods", PodCLHandler), (r"/api/v1/pods/([^/]+)", PodGURHandler), - (r"/api/v1/projects", ProjectHandler), - (r"/api/v1/projects/([^/]+)", ProjectHandler), + (r"/api/v1/projects", ProjectCLHandler), + (r"/api/v1/projects/([^/]+)", ProjectGURHandler), (r"/api/v1/projects/([^/]+)/cases", TestcaseHandler), (r"/api/v1/projects/([^/]+)/cases/([^/]+)", TestcaseHandler), (r"/api/v1/results", TestResultsHandler), diff --git a/utils/test/result_collection_api/tests/unit/test_project.py b/utils/test/result_collection_api/tests/unit/test_project.py index c38078098..b07cb7ad6 100644 --- a/utils/test/result_collection_api/tests/unit/test_project.py +++ b/utils/test/result_collection_api/tests/unit/test_project.py @@ -30,6 +30,18 @@ class TestProjectCreate(TestProjectBase): (code, body) = self.create() self.assertEqual(code, HTTP_BAD_REQUEST) + def test_emptyName(self): + req_empty = ProjectCreateRequest('') + (code, body) = self.create(req_empty) + self.assertEqual(code, HTTP_BAD_REQUEST) + self.assertIn('project name missing', body) + + def test_noneName(self): + req_none = ProjectCreateRequest(None) + (code, body) = self.create(req_none) + self.assertEqual(code, HTTP_BAD_REQUEST) + self.assertIn('project name missing', body) + def test_success(self): (code, body) = self.create_d() self.assertEqual(code, HTTP_OK) -- cgit 1.2.3-korg