Flask + jsonschema experiments

This commit is contained in:
Alexander Kislitsky 2014-09-18 14:56:42 +04:00
parent 0640d4bba3
commit 77817b497c
16 changed files with 222 additions and 22 deletions

View File

@ -1,5 +1,15 @@
from flask import Flask
app = Flask(__name__)
import flask
import flask_jsonschema
import os
app = flask.Flask(__name__)
app.config['JSONSCHEMA_DIR'] = os.path.join(app.root_path, 'schemas')
flask_jsonschema.JsonSchema(app)
# Application errors handling
from collector.api import error_handling
# Application resources handling
from collector.api.resources import action_logs

View File

@ -0,0 +1,28 @@
from flask import current_app, jsonify
from functools import wraps
import jsonschema
def handle_response(http_code, *path):
"""Checks response, if VALIDATE_RESPONSE in app.config is set to True
and path is not empty.
Jsonifies response, adds http_code to returning values.
:param http_code:
:type http_code: integer
:param path: path to response json schema
:type path: collection of strings
:return: tuple of jsonifyed response and http_code
"""
def wrapper(fn):
@wraps(fn)
def decorated(*args, **kwargs):
response = fn(*args, **kwargs)
print "### handle_response", response
if current_app.config.get('VALIDATE_RESPONSE', False) and path:
print "### validating response"
jschema = current_app.extensions.get('jsonschema')
jsonschema.validate(response, jschema.get_schema(path))
return jsonify(response), http_code
return decorated
return wrapper

View File

@ -0,0 +1,11 @@
class Production(object):
DEBUG = False
PORT = 5000
HOST = 'localhost'
VALIDATE_RESPONSE = False
class Testing(Production):
DEBUG = True
HOST = '0.0.0.0'
VALIDATE_RESPONSE = True

View File

@ -0,0 +1,34 @@
from flask import jsonify
from flask import make_response
import flask_jsonschema
from collector.api.app import app
@app.errorhandler(400)
def bad_request(error):
print "### bad request"
return make_response(jsonify({'status': 'error', 'message': '{}'.format(error)}), 400)
@app.errorhandler(404)
def not_found(error):
print "### not_found"
return make_response(jsonify({'status': 'error', 'message': '{}'.format(error)}), 404)
@app.errorhandler(flask_jsonschema.ValidationError)
def validation_error(error):
print "### validation_error"
return make_response(jsonify({'status': 'error', 'message': '{}'.format(error)}), 400)
@app.errorhandler(500)
def server_error(error):
print "### server_error"
return make_response(jsonify({'status': 'error', 'message': '{0}: {1}'.format(error.__class__.__name__, error)}), 500)

View File

@ -0,0 +1,21 @@
from flask import jsonify
from flask import request
from flask_jsonschema import validate as validate_request
from collector.api.app import app
from collector.api.common.util import handle_response
@app.route('/api/v1/action_logs', methods=['POST'])
@validate_request('action_logs', 'post_request')
@handle_response(201, 'action_logs', 'post_response')
def post():
print "### request.data", request.data
print "### request.headers", request.headers
print "### app.config.VALIDATE_RESPONSE", app.config.get('VALIDATE_RESPONSE', False)
return {'status': 'ok'}
@app.route('/api/v1/action_logs', methods=['GET'])
def get():
return jsonify({'get': 'ok'}), 200

View File

@ -1,7 +0,0 @@
def fib(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
return a

View File

@ -0,0 +1,23 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"post_request": {
"type": "object",
"properties": {
"id": { "type": "integer" }
},
"required": ["id"]
},
"post_response": {
"type": "object",
"properties": {
"status": {"enum": ["ok", "error"]},
"message": {"type": "string"}
},
"required": ["status"]
}
}

View File

@ -0,0 +1,46 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"post_req": {
"definitions": {
"positive_number": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true
}
},
"type": "object",
"properties": {
"id": { "$ref": "#/definitions/positive_number" },
"title": {},
"author": {}
},
"required": ["id"]
},
"type": "object",
"def": {
"status_resp": {
"type": "object",
"properties": {
"status": {"enum": ["ok", "error"]},
"message": {"type": "string"}
},
"required": ["status"]
}
},
"post_resp": {
"allOf": [
{
"$ref": "#/def/status_resp"
},
{
"properties": {
"message": {"type": "string"}
}
}
]
}
}

View File

@ -1,5 +0,0 @@
from flask import Flask
app = Flask(__name__)
from collector.app import views

View File

@ -1,6 +0,0 @@
from collector.app import app
@app.route('/ping')
def ping():
return 'ok'

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,23 @@
from flask import json
from unittest2.case import TestCase
from collector.api import app
class BaseTest(TestCase):
@classmethod
def setUpClass(cls):
app.app.config.from_object('collector.api.config.Testing')
def setUp(self):
self.client = app.app.test_client()
def post(self, url, data):
return self.client.post(url, data=json.dumps(data),
content_type='application/json')
def test_unknown_resource(self):
resp = self.client.get('/xxx')
print "### resp", resp

View File

@ -0,0 +1,17 @@
from collector.test.base import BaseTest
class TestActionLogs(BaseTest):
def test_post(self):
resp = self.post('/api/v1/action_logs', {'id': 1})
print "### resp.status_code", resp.status_code
print "### resp.headers", resp.headers
print "### resp.data", resp.data
print "### resp", resp
# resp = self.client.get('/api/v1/action_logs')
# # from collector.api.app import api
# # print "### resources", api.resources
# print "### resp.status_code", resp.status_code
# print "### resp.headers", resp.headers
# print "### resp.data", resp.data

View File

@ -1 +1,2 @@
flask
flask-jsonschema

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python
from collector.app import app
from collector.api.app import app
app.run(debug=True)

View File

@ -0,0 +1,3 @@
-r requirements.txt
unittest2
mock