Add api versioning

This commit is contained in:
efedorova 2013-10-11 13:17:35 +04:00
parent eb3b446a9f
commit f40fe7372c
5 changed files with 132 additions and 257 deletions

209
api/v1.py
View File

@ -1,105 +1,140 @@
# Copyright (c) 2013 Mirantis, Inc.
# Copyright (c) 2013 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from flask import Flask
from flask.ext.restful import reqparse, abort, Api, Resource
import os
from flask import Blueprint, make_response, send_file
from flask import jsonify, request, abort
from flask import current_app
from werkzeug import secure_filename
app = Flask(__name__)
api = Api(app)
from parser import ManifestParser
from archiver import Archiver
TODOS = {
'todo1': {'task': 'build an API'},
'todo2': {'task': '?????'},
'todo3': {'task': 'profit!'},
}
v1_api = Blueprint('v1', __name__)
def abort_if_todo_doesnt_exist(todo_id):
if todo_id not in TODOS:
abort(404, message="Todo {} doesn't exist".format(todo_id))
@v1_api.route('/client/ui')
def get_ui_data():
parser = ManifestParser(current_app.config["ROOT_DIRECTORY"])
manifests = parser.parse()
archive_name = Archiver().create(manifests, "ui_forms")
parser = reqparse.RequestParser()
parser.add_argument('task', type=str)
return send_file(archive_name)
# Todo
# show a single todo item and lets you delete them
class Todo(Resource):
def get(self, todo_id):
abort_if_todo_doesnt_exist(todo_id)
return TODOS[todo_id]
@v1_api.route('/client/conductor')
def get_conductor_data():
parser = ManifestParser(current_app.config["ROOT_DIRECTORY"])
manifests = parser.parse()
archive_name = Archiver().create(manifests,
"heat_templates",
"agent_templates",
"scripts")
def delete(self, todo_id):
abort_if_todo_doesnt_exist(todo_id)
del TODOS[todo_id]
return '', 204
def put(self, todo_id):
args = parser.parse_args()
task = {'task': args['task']}
TODOS[todo_id] = task
return task, 201
return send_file(archive_name)
# TodoList
# shows a list of all todos, and lets you POST to add new tasks
class TodoList(Resource):
def get(self):
return TODOS
@v1_api.route('/admin/<data_type>', methods=['GET', 'POST'])
def get_data_type_locations(data_type):
####### validation ########
if data_type not in current_app.config['DATA_TYPES']:
abort(404)
result_path = os.path.join(current_app.config["DIRECTORIES_BY_TYPE"][
data_type])
####### end validation ########
if request.method == 'GET':
locations = []
def post(self):
args = parser.parse_args()
todo_id = 'todo%d' % (len(TODOS) + 1)
TODOS[todo_id] = {'task': args['task']}
return TODOS[todo_id], 201
for path, subdirs, files in os.walk(result_path):
for name in files:
locations.append(os.path.join(path, name))
result = {data_type: locations}
return jsonify(result)
##
## Actually setup the Api resource routing here
##
api.add_resource(TodoList, '/todos')
api.add_resource(Todo, '/todos/<string:todo_id>')
if request.method == 'POST':
file_to_upload = request.files.get('file')
if file_to_upload:
filename = secure_filename(file_to_upload.filename)
file_to_upload.save(os.path.join(result_path, filename))
return jsonify(result="success")
else:
abort(503)
if __name__ == '__main__':
app.run(debug=True)
#
# @app.route('/client/ui')
# def ui_data():
# #type - application/z-gzip
# pass
#
# @app.route('/client/ui')
# def conductor_data():
# #type - application/z-gzip
# pass
#
# @app.route('/client/ui')
# def conductor_data():
# #type - application/json
# pass
#
# @app.route('/client/<data_type>/<path>')
# def conductor_data():
# #type - application/json
# pass
#
# @app.route('/client/<data_type>/<file_name>')
# def conductor_data():
# #type - application/json
# pass
#
#
# if __name__ == '__main__':
# app.run()
@v1_api.route('/admin/<data_type>/<path:path>', methods=['GET', 'POST'])
def get_data_type_locations_by_path_or_get_file(data_type, path):
####### validation ########
if data_type not in current_app.config['DATA_TYPES']:
abort(404)
result_path = os.path.join(current_app.config["DIRECTORIES_BY_TYPE"][
data_type],
path)
if not os.path.exists(result_path):
abort(404)
####### end validation ########
if request.method == 'GET':
locations = []
#return file content or directory content
if os.path.isfile(result_path):
return send_file(result_path)
else:
for file in os.listdir(result_path):
locations.append(os.path.join(path, file))
result = {data_type: locations}
return jsonify(result)
if request.method == 'POST':
file_to_upload = request.files.get('file')
if file_to_upload:
filename = secure_filename(file_to_upload.filename)
file_to_upload.save(os.path.join(result_path, filename))
return jsonify(result="success")
else:
abort(403)
@v1_api.route('/admin/<data_type>/<path:path>', methods=['PUT', 'DELETE'])
def create_dirs(data_type, path):
if data_type not in current_app.config['DATA_TYPES']:
abort(404)
result_path = os.path.join(current_app.config["DIRECTORIES_BY_TYPE"][
data_type],
path)
if request.method == 'PUT':
resp = make_response()
if os.path.exists(result_path):
return resp
if data_type == current_app.config['MANIFESTS']:
abort(403)
try:
os.makedirs(result_path)
except Exception as e:
abort(403)
return resp
if request.method == 'DELETE':
if not os.path.exists(result_path):
abort(404)
if os.path.isfile(result_path):
try:
os.remove(result_path)
except Exception as e:
abort(404)
else:
try:
os.rmdir(result_path)
except Exception as e:
abort(403)
resp = make_response()
return resp

View File

@ -87,7 +87,7 @@ class Archiver(object):
shutil.rmtree(temp_dir, ignore_errors=True)
except Exception as e:
log.error("Unable to delete temp directory: {0}".format(e))
return target_archeve
return os.path.abspath(target_archeve)

View File

@ -1,13 +0,0 @@
# Copyright (c) 2013 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

View File

@ -11,18 +11,16 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
from parser import ManifestParser
from archiver import Archiver
import flask
from keystoneclient.middleware import auth_token
from api.v1 import v1_api
def main():
parser = ManifestParser(os.path.join(os.path.dirname(__file__), os.pardir,
'Services'))
manifests = parser.parse()
Archiver().create(manifests, "ui_forms", "heat_templates", "agent_templates")
app = flask.Flask(__name__)
if __name__ == "__main__":
main()
app.register_blueprint(v1_api, url_prefix='/v1')
app.config.from_pyfile('consts.py')
if __name__ == '__main__':
app.run(debug=True)

145
test.py
View File

@ -1,145 +0,0 @@
# Copyright (c) 2013 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
from flask import Flask, make_response, send_from_directory, send_file, \
jsonify, request, abort
from flask.ext.restful import Api, Resource
from werkzeug import secure_filename
from parser import ManifestParser
from archiver import Archiver
app = Flask(__name__)
app.config.from_pyfile('consts.py')
api = Api(app)
@app.route('/client/ui')
def get_ui_data():
parser = ManifestParser(app.config["ROOT_DIRECTORY"])
manifests = parser.parse()
archive_name = Archiver().create(manifests, "ui_forms")
return send_from_directory(os.path.dirname(__file__), archive_name)
@app.route('/client/conductor')
def get_conductor_data():
parser = ManifestParser(app.config["ROOT_DIRECTORY"])
manifests = parser.parse()
archive_name = Archiver().create(manifests,
"heat_templates",
"agent_templates",
"scripts")
return send_from_directory(os.path.dirname(__file__), archive_name)
@app.route('/admin/<data_type>', methods=['GET', 'POST'])
def get_data_type_locations(data_type):
####### validation ########
if data_type not in app.config['DATA_TYPES']:
abort(404)
result_path = os.path.join(app.config["DIRECTORIES_BY_TYPE"][
data_type])
####### end validation ########
if request.method == 'GET':
locations = []
for path, subdirs, files in os.walk(result_path):
for name in files:
locations.append(os.path.join(path, name))
result = {data_type: locations}
return jsonify(result)
if request.method == 'POST':
file_to_upload = request.files.get('file')
if file_to_upload:
filename = secure_filename(file_to_upload.filename)
file_to_upload.save(os.path.join(result_path, filename))
return jsonify(result="success")
else:
abort(503)
@app.route('/admin/<data_type>/<path:path>', methods=['GET', 'POST'])
def get_data_type_locations_by_path_or_get_file(data_type, path):
####### validation ########
if data_type not in app.config['DATA_TYPES']:
abort(404)
result_path = os.path.join(app.config["DIRECTORIES_BY_TYPE"][
data_type],
path)
if not os.path.exists(result_path):
abort(404)
####### end validation ########
if request.method == 'GET':
locations = []
#return file content or directory content
if os.path.isfile(result_path):
return send_file(result_path)
else:
for file in os.listdir(result_path):
locations.append(os.path.join(path, file))
result = {data_type: locations}
return jsonify(result)
if request.method == 'POST':
file_to_upload = request.files.get('file')
if file_to_upload:
filename = secure_filename(file_to_upload.filename)
file_to_upload.save(os.path.join(result_path, filename))
return jsonify(result="success")
else:
abort(403)
@app.route('/admin/<data_type>/<path:path>', methods=['PUT', 'DELETE'])
def create_dirs(data_type, path):
if data_type not in app.config['DATA_TYPES']:
abort(404)
result_path = os.path.join(app.config["DIRECTORIES_BY_TYPE"][
data_type],
path)
if request.method == 'PUT':
resp = make_response()
if os.path.exists(result_path):
return resp
if data_type == app.config['MANIFESTS']:
abort(403)
try:
os.makedirs(result_path)
except Exception as e:
abort(403)
return resp
if request.method == 'DELETE':
if not os.path.exists(result_path):
abort(404)
if os.path.isfile(result_path):
try:
os.remove(result_path)
except Exception as e:
abort(404)
else:
try:
os.rmdir(result_path)
except Exception as e:
abort(403)
resp = make_response()
return resp
if __name__ == '__main__':
app.run(debug=True)
#by default server running on localhost:5000