summaryrefslogtreecommitdiffstats
path: root/dashboard/backend/dovetail/api
diff options
context:
space:
mode:
authorLeo Wang <grakiss.wanglei@huawei.com>2016-11-16 22:44:23 -0500
committerLeo Wang <grakiss.wanglei@huawei.com>2016-11-20 23:00:24 -0500
commit68fde29bdcfe0b206f588dab85e5b7d8ac9449f4 (patch)
tree833c95a1af1379a011d8c64728369718fe8edbdd /dashboard/backend/dovetail/api
parent8a6bc92ff3b906a72194c7fa5db61ebb030052a4 (diff)
Backend rest api mechanism
JIRA:DOVETAIL-63 provide rest api as the dashboard backend 1. using gunicorn as rest api server 2. using flask as rest api framework 3. using sqlalchemy as mysql database driver 4. implement basic report CRUD operations 5. implement basic session management in database operations Change-Id: Ifbd251462396c2cb414b1ae9150cfc1e2e2d00c0 Signed-off-by: Leo Wang <grakiss.wanglei@huawei.com>
Diffstat (limited to 'dashboard/backend/dovetail/api')
-rwxr-xr-xdashboard/backend/dovetail/api/__init__.py29
-rwxr-xr-xdashboard/backend/dovetail/api/api.py182
-rwxr-xr-xdashboard/backend/dovetail/api/exception_handler.py93
-rwxr-xr-xdashboard/backend/dovetail/api/utils.py20
4 files changed, 324 insertions, 0 deletions
diff --git a/dashboard/backend/dovetail/api/__init__.py b/dashboard/backend/dovetail/api/__init__.py
new file mode 100755
index 00000000..f9c4e5a2
--- /dev/null
+++ b/dashboard/backend/dovetail/api/__init__.py
@@ -0,0 +1,29 @@
+##############################################################################
+# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import datetime
+import logging
+
+from flask import Flask
+
+from dovetail.utils import util
+
+logging.info('flask app: begin to init')
+
+app = Flask(__name__)
+app.debug = True
+logging.info('flask app config:%s', app.config)
+
+app.config['REMEMBER_COOKIE_DURATION'] = (
+ datetime.timedelta(
+ seconds=util.parse_time_interval('2h')
+ )
+)
+
+logging.info('flask app: finish init')
diff --git a/dashboard/backend/dovetail/api/api.py b/dashboard/backend/dovetail/api/api.py
new file mode 100755
index 00000000..0f405f23
--- /dev/null
+++ b/dashboard/backend/dovetail/api/api.py
@@ -0,0 +1,182 @@
+##############################################################################
+# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import logging
+
+from dovetail.api import utils
+from dovetail.api import exception_handler
+from dovetail.db import api as db_api
+
+from flask import Flask
+from flask import request
+
+import json
+
+app = Flask(__name__)
+
+
+@app.after_request
+def after_request(response):
+ response.headers.add('Access-Control-Allow-Origin', '*')
+ response.headers.add(
+ 'Access-Control-Allow-Headers',
+ 'Content-Type, Authorization')
+ response.headers.add('Aceess-Control-Allow-Methods', 'GET,PUT,DELETE,POST')
+ return response
+
+# test
+
+
+@app.route("/test", methods=['GET'])
+def test():
+ """backend api test"""
+ logging.info('test functest')
+ resp = utils.make_json_response(
+ 200, {'test': 20}
+ )
+ return resp
+
+
+# settings
+@app.route("/clear", methods=['POST'])
+def clear_settings():
+ """ clear all settings data on backend server """
+ logging.info('clear all settings')
+
+ return utils.make_json_response(
+ 200, {}
+ )
+
+
+@app.route("/settings", methods=['GET'])
+def list_settings():
+ """list settings"""
+ logging.info('list settings')
+ global settings
+ return utils.make_json_response(200, settings)
+
+
+@app.route("/settings", methods=['POST'])
+def add_settings():
+ pass
+
+
+@app.route("/settings", methods=['POST'])
+def remove_settings():
+ pass
+
+
+@app.route("/testcases", methods=['GET'])
+def get_testcases():
+ pass
+
+
+@app.route("/results/<test_id>", methods=['GET'])
+def show_result(test_id):
+ data = _get_request_args()
+ return utils.make_json_response(
+ 200,
+ db_api.get_result(
+ test_id, **data
+ )
+ )
+
+
+@app.route("/results", methods=['GET'])
+def list_results():
+ data = _get_request_args()
+ return utils.make_json_response(
+ 200,
+ db_api.list_results(
+ **data
+ )
+ )
+
+
+@app.route("/results", methods=['POST'])
+def add_result():
+ data = _get_request_data()
+ ret_code = 200
+ json_object = json.loads(data)
+ logging.debug('json_object:%s' % (json_object))
+ if not db_api.store_result(**json_object):
+ ret_code = 500
+ resp = utils.make_json_response(
+ ret_code, data
+ )
+ return resp
+
+
+@app.route("/results/<test_id>", methods=['DELETE'])
+def remove_results(test_id):
+ data = _get_request_data()
+ logging.debug('data:%s' % data)
+ response = db_api.del_result(
+ test_id, **data
+ )
+ return utils.make_json_response(
+ 200, response
+ )
+
+
+def _get_request_data():
+ """Convert reqeust data from string to python dict.
+
+ If the request data is not json formatted, raises
+ exception_handler.BadRequest.
+ If the request data is not json formatted dict, raises
+ exception_handler.BadRequest
+ If the request data is empty, return default as empty dict.
+
+ Usage: It is used to add or update a single resource.
+ """
+ if request.data:
+ try:
+ data = json.loads(request.data)
+ except Exception:
+ raise exception_handler.BadRequest(
+ 'request data is not json formatted: %s' % request.data
+ )
+ if not isinstance(data, dict):
+ raise exception_handler.BadRequest(
+ 'request data is not json formatted dict: %s' % request.data
+ )
+
+ return request.data
+ else:
+ return {}
+
+
+def _get_request_args(**kwargs):
+ """Get request args as dict.
+
+ The value in the dict is converted to expected type.
+
+ Args:
+ kwargs: for each key, the value is the type converter.
+ """
+ args = dict(request.args)
+ for key, value in args.items():
+ if key in kwargs:
+ converter = kwargs[key]
+ if isinstance(value, list):
+ args[key] = [converter(item) for item in value]
+ else:
+ args[key] = converter(value)
+ return args
+
+'''
+@app.teardown_appcontext
+def shutdown_session(exception=None):
+ db_session.remove()
+'''
+# user login/logout
+
+if __name__ == '__main__':
+ app.run(host='127.0.0.1')
diff --git a/dashboard/backend/dovetail/api/exception_handler.py b/dashboard/backend/dovetail/api/exception_handler.py
new file mode 100755
index 00000000..b7ce592a
--- /dev/null
+++ b/dashboard/backend/dovetail/api/exception_handler.py
@@ -0,0 +1,93 @@
+##############################################################################
+# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+"""Exceptions for RESTful API."""
+import traceback
+
+from dovetail.api import app
+from dovetail.api import utils
+
+
+class HTTPException(Exception):
+
+ def __init__(self, message, status_code):
+ super(HTTPException, self).__init__(message)
+ self.traceback = traceback.format_exc()
+ self.status_code = status_code
+
+ def to_dict(self):
+ return {'message': str(self)}
+
+
+class ItemNotFound(HTTPException):
+ """Define the exception for referring non-existing object."""
+
+ def __init__(self, message):
+ super(ItemNotFound, self).__init__(message, 410)
+
+
+class BadRequest(HTTPException):
+ """Define the exception for invalid/missing parameters.
+
+ User making a request in invalid state cannot be processed.
+ """
+
+ def __init__(self, message):
+ super(BadRequest, self).__init__(message, 400)
+
+
+class Unauthorized(HTTPException):
+ """Define the exception for invalid user login."""
+
+ def __init__(self, message):
+ super(Unauthorized, self).__init__(message, 401)
+
+
+class UserDisabled(HTTPException):
+ """Define the exception for disabled users."""
+
+ def __init__(self, message):
+ super(UserDisabled, self).__init__(message, 403)
+
+
+class Forbidden(HTTPException):
+ """Define the exception for invalid permissions."""
+
+ def __init__(self, message):
+ super(Forbidden, self).__init__(message, 403)
+
+
+class BadMethod(HTTPException):
+ """Define the exception for invoking unsupported methods."""
+
+ def __init__(self, message):
+ super(BadMethod, self).__init__(message, 405)
+
+
+class ConflictObject(HTTPException):
+ """Define the exception for creating an existing object."""
+
+ def __init__(self, message):
+ super(ConflictObject, self).__init__(message, 409)
+
+
+@app.errorhandler(Exception)
+def handle_exception(error):
+ if hasattr(error, 'to_dict'):
+ response = error.to_dict()
+ else:
+ response = {'message': str(error)}
+ if app.debug and hasattr(error, 'traceback'):
+ response['traceback'] = error.traceback
+
+ status_code = 400
+ if hasattr(error, 'status_code'):
+ status_code = error.status_code
+
+ return utils.make_json_response(status_code, response)
diff --git a/dashboard/backend/dovetail/api/utils.py b/dashboard/backend/dovetail/api/utils.py
new file mode 100755
index 00000000..dbe8d082
--- /dev/null
+++ b/dashboard/backend/dovetail/api/utils.py
@@ -0,0 +1,20 @@
+##############################################################################
+# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import json
+from flask import make_response
+
+
+def make_json_response(status_code, data):
+ """Wrap json format to the reponse object."""
+
+ result = json.dumps(data, indent=4, default=lambda x: None) + '\r\n'
+ resp = make_response(result, status_code)
+ resp.headers['Content-type'] = 'application/json'
+ return resp