Pretty-print format all JSON response bodies

Flask-RESTful automatically converts a response body to JSON when it
creates the Response object. The Flask app configuration option
RESTFUL_JSON is added by this commit to appropriately modify how the
JSON body is constructed.

response_filter has been modified so that it returns a tuple of
information that Flask-RESTful will use to create an appropriately
formatted response.

Resource.error_response is creating the Response object itself and so
has been modified to format the JSON in the same way as Flask-RESTful.
Ideally, error_response would not create a separate Response object
however addressing this is a separate issue.

Change-Id: I02eafaf48d7c6e2ceab0add2f78cf6aa1b890f18
Closes-bug: 1664328
This commit is contained in:
git-harry 2017-03-07 15:33:08 +00:00
parent 7258e8e739
commit 0f7ee6d791
4 changed files with 54 additions and 23 deletions

View File

@ -1,6 +1,7 @@
from datetime import date
import os
from paste import deploy
from flask import Flask
from flask import Flask, json
from oslo_config import cfg
@ -48,10 +49,27 @@ def create_app(global_config, **local_config):
return setup_app()
class JSONEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, date):
return o.isoformat()
return json.JSONEncoder.default(self, o)
RESTFUL_JSON = {
"indent": 2,
"sort_keys": True,
"cls": JSONEncoder,
"separators": (",", ": "),
}
def setup_app(config=None):
app = Flask(__name__, static_folder=None)
app.config.update(
PROPAGATE_EXCEPTIONS=True
PROPAGATE_EXCEPTIONS=True,
RESTFUL_JSON=RESTFUL_JSON,
)
app.register_blueprint(v1.bp, url_prefix='/v1')
return app

View File

@ -1,5 +1,6 @@
import functools
import inspect
import json
import re
import urllib.parse as urllib
@ -8,6 +9,7 @@ import decorator
import flask
import flask_restful as restful
from craton import api
from craton.api.v1.validators import ensure_project_exists
from craton.api.v1.validators import request_validate
from craton.api.v1.validators import response_filter
@ -22,10 +24,14 @@ class Resource(restful.Resource):
response_filter]
def error_response(self, status_code, message):
resp = flask.jsonify({
'status': status_code,
'message': message
})
body = json.dumps(
{
'status': status_code,
'message': message
},
**api.RESTFUL_JSON,
)
resp = flask.make_response("{body}\n".format(body=body))
resp.status_code = status_code
return resp

View File

@ -1,11 +1,10 @@
# The code is auto generated, your change will be overwritten by
# code generating.
from datetime import date
from functools import wraps
from werkzeug.datastructures import MultiDict, Headers
from flask import request, current_app, json
from flask import request, current_app
from flask_restful import abort
from flask_restful.utils import unpack
from jsonschema import Draft4Validator
@ -143,14 +142,6 @@ def normalize(schema, data, required_defaults=None):
return _normalize(schema, data), errors
class JSONEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, date):
return o.isoformat()
return json.JSONEncoder.default(self, o)
class FlaskValidatorAdaptor(object):
def __init__(self, schema):
@ -282,13 +273,7 @@ def response_filter(view):
if errors:
abort(500, message='Expectation Failed', errors=errors)
return current_app.response_class(
json.dumps(resp, cls=JSONEncoder) + '\n',
status=status,
headers=headers,
mimetype='application/json'
)
return resp, status, headers
return wrapper

View File

@ -1,5 +1,6 @@
import contextlib
import docker
import json
import requests
from retrying import retry
from sqlalchemy import create_engine
@ -220,28 +221,49 @@ class TestCase(testtools.TestCase):
def assertBadRequest(self, response):
self.assertEqual(requests.codes.BAD_REQUEST, response.status_code)
def assertJSON(self, response):
if response.text:
try:
data = json.loads(response.text)
except json.JSONDecodeError:
self.fail("Response data is not JSON.")
else:
reference = "{formatted_data}\n".format(
formatted_data=json.dumps(
data, indent=2, sort_keys=True, separators=(',', ': ')
)
)
self.assertEqual(
reference,
response.text
)
def get(self, url, headers=None, **params):
resp = self.session.get(
url, verify=False, headers=headers, params=params,
)
self.assertJSON(resp)
return resp
def post(self, url, headers=None, data=None):
resp = self.session.post(
url, verify=False, headers=headers, json=data,
)
self.assertJSON(resp)
return resp
def put(self, url, headers=None, data=None):
resp = self.session.put(
url, verify=False, headers=headers, json=data,
)
self.assertJSON(resp)
return resp
def delete(self, url, headers=None, body=None):
resp = self.session.delete(
url, verify=False, headers=headers, json=body,
)
self.assertJSON(resp)
return resp
def create_cloud(self, name, variables=None):