Refactor to separate serializers from wsgi controller
Remove the serializers from heat.common.wsgi, so we break the circular import which happens if you want to import heat.api.aws.exceptions to do a determination based on exception type, which is required to avoid the faultwrap exception disguise which is not applicable to the CFN API. Partial-Bug: #1291079 Change-Id: I7498d78f8ec6098b28fb183eaaa04aa81fced3eb
This commit is contained in:
parent
15892aac16
commit
ba48137e24
|
@ -18,7 +18,7 @@
|
|||
|
||||
import webob.exc
|
||||
|
||||
from heat.common import wsgi
|
||||
from heat.common import serializers
|
||||
from heat.openstack.common.gettextutils import _
|
||||
from heat.openstack.common.rpc import common as rpc_common
|
||||
|
||||
|
@ -44,7 +44,7 @@ class HeatAPIException(webob.exc.HTTPError):
|
|||
paste pipeline. We serialize in XML by default (as AWS does)
|
||||
'''
|
||||
webob.exc.HTTPError.__init__(self, detail=detail)
|
||||
serializer = wsgi.XMLResponseSerializer()
|
||||
serializer = serializers.XMLResponseSerializer()
|
||||
serializer.default(self, self.get_unserialized_body())
|
||||
|
||||
def get_unserialized_body(self):
|
||||
|
|
|
@ -27,6 +27,7 @@ import webob
|
|||
|
||||
cfg.CONF.import_opt('debug', 'heat.openstack.common.log')
|
||||
|
||||
from heat.common import serializers
|
||||
from heat.common import exception
|
||||
from heat.openstack.common import log as logging
|
||||
import heat.openstack.common.rpc.common as rpc_common
|
||||
|
@ -44,9 +45,9 @@ class Fault(object):
|
|||
@webob.dec.wsgify(RequestClass=wsgi.Request)
|
||||
def __call__(self, req):
|
||||
if req.content_type == 'application/xml':
|
||||
serializer = wsgi.XMLResponseSerializer()
|
||||
serializer = serializers.XMLResponseSerializer()
|
||||
else:
|
||||
serializer = wsgi.JSONResponseSerializer()
|
||||
serializer = serializers.JSONResponseSerializer()
|
||||
resp = webob.Response(request=req)
|
||||
default_webob_exc = webob.exc.HTTPInternalServerError()
|
||||
resp.status_code = self.error.get('code', default_webob_exc.code)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
from webob import exc
|
||||
|
||||
from heat.api.openstack.v1 import util
|
||||
from heat.common import serializers
|
||||
from heat.common import wsgi
|
||||
from heat.rpc import client as rpc_client
|
||||
|
||||
|
@ -62,5 +63,5 @@ def create_resource(options):
|
|||
Actions action factory method.
|
||||
"""
|
||||
deserializer = wsgi.JSONRequestDeserializer()
|
||||
serializer = wsgi.JSONResponseSerializer()
|
||||
serializer = serializers.JSONResponseSerializer()
|
||||
return wsgi.Resource(ActionController(options), deserializer, serializer)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
from oslo.config import cfg
|
||||
|
||||
from heat.api.openstack.v1 import util
|
||||
from heat.common import serializers
|
||||
from heat.common import wsgi
|
||||
from heat.rpc import client as rpc_client
|
||||
|
||||
|
@ -46,6 +47,6 @@ def create_resource(options):
|
|||
BuildInfo factory method.
|
||||
"""
|
||||
deserializer = wsgi.JSONRequestDeserializer()
|
||||
serializer = wsgi.JSONResponseSerializer()
|
||||
serializer = serializers.JSONResponseSerializer()
|
||||
return wsgi.Resource(BuildInfoController(options), deserializer,
|
||||
serializer)
|
||||
|
|
|
@ -17,6 +17,7 @@ from webob import exc
|
|||
|
||||
from heat.api.openstack.v1 import util
|
||||
from heat.common import identifier
|
||||
from heat.common import serializers
|
||||
from heat.common import wsgi
|
||||
from heat.rpc import api as engine_api
|
||||
from heat.rpc import client as rpc_client
|
||||
|
@ -128,5 +129,5 @@ def create_resource(options):
|
|||
Events resource factory method.
|
||||
"""
|
||||
deserializer = wsgi.JSONRequestDeserializer()
|
||||
serializer = wsgi.JSONResponseSerializer()
|
||||
serializer = serializers.JSONResponseSerializer()
|
||||
return wsgi.Resource(EventController(options), deserializer, serializer)
|
||||
|
|
|
@ -15,6 +15,7 @@ import itertools
|
|||
|
||||
from heat.api.openstack.v1 import util
|
||||
from heat.common import identifier
|
||||
from heat.common import serializers
|
||||
from heat.common import wsgi
|
||||
from heat.rpc import api as engine_api
|
||||
from heat.rpc import client as rpc_client
|
||||
|
@ -113,5 +114,5 @@ def create_resource(options):
|
|||
Resources resource factory method.
|
||||
"""
|
||||
deserializer = wsgi.JSONRequestDeserializer()
|
||||
serializer = wsgi.JSONResponseSerializer()
|
||||
serializer = serializers.JSONResponseSerializer()
|
||||
return wsgi.Resource(ResourceController(options), deserializer, serializer)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
from webob import exc
|
||||
|
||||
from heat.api.openstack.v1 import util
|
||||
from heat.common import serializers
|
||||
from heat.common import wsgi
|
||||
from heat.rpc import client as rpc_client
|
||||
|
||||
|
@ -77,6 +78,6 @@ def create_resource(options):
|
|||
Software configs resource factory method.
|
||||
"""
|
||||
deserializer = wsgi.JSONRequestDeserializer()
|
||||
serializer = wsgi.JSONResponseSerializer()
|
||||
serializer = serializers.JSONResponseSerializer()
|
||||
return wsgi.Resource(
|
||||
SoftwareConfigController(options), deserializer, serializer)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
from webob import exc
|
||||
|
||||
from heat.api.openstack.v1 import util
|
||||
from heat.common import serializers
|
||||
from heat.common import wsgi
|
||||
from heat.rpc import client as rpc_client
|
||||
|
||||
|
@ -109,6 +110,6 @@ def create_resource(options):
|
|||
Software deployments resource factory method.
|
||||
"""
|
||||
deserializer = wsgi.JSONRequestDeserializer()
|
||||
serializer = wsgi.JSONResponseSerializer()
|
||||
serializer = serializers.JSONResponseSerializer()
|
||||
return wsgi.Resource(
|
||||
SoftwareDeploymentController(options), deserializer, serializer)
|
||||
|
|
|
@ -21,6 +21,7 @@ from heat.api.openstack.v1 import util
|
|||
from heat.api.openstack.v1.views import stacks_view
|
||||
from heat.common import environment_format
|
||||
from heat.common import identifier
|
||||
from heat.common import serializers
|
||||
from heat.common import template_format
|
||||
from heat.common import urlfetch
|
||||
from heat.common import wsgi
|
||||
|
@ -378,7 +379,7 @@ class StackController(object):
|
|||
return self.rpc_client.generate_template(req.context, type_name)
|
||||
|
||||
|
||||
class StackSerializer(wsgi.JSONResponseSerializer):
|
||||
class StackSerializer(serializers.JSONResponseSerializer):
|
||||
"""Handles serialization of specific controller method responses."""
|
||||
|
||||
def _populate_response_header(self, response, location, status):
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
#
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# Copyright 2013 IBM Corp.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Utility methods for serializing responses
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import json
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from heat.openstack.common import log as logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class JSONResponseSerializer(object):
|
||||
|
||||
def to_json(self, data):
|
||||
def sanitizer(obj):
|
||||
if isinstance(obj, datetime.datetime):
|
||||
return obj.isoformat()
|
||||
return obj
|
||||
|
||||
response = json.dumps(data, default=sanitizer)
|
||||
logger.debug("JSON response : %s" % response)
|
||||
return response
|
||||
|
||||
def default(self, response, result):
|
||||
response.content_type = 'application/json'
|
||||
response.body = self.to_json(result)
|
||||
|
||||
|
||||
# Escape XML serialization for these keys, as the AWS API defines them as
|
||||
# JSON inside XML when the response format is XML.
|
||||
JSON_ONLY_KEYS = ('TemplateBody', 'Metadata')
|
||||
|
||||
|
||||
class XMLResponseSerializer(object):
|
||||
|
||||
def object_to_element(self, obj, element):
|
||||
if isinstance(obj, list):
|
||||
for item in obj:
|
||||
subelement = etree.SubElement(element, "member")
|
||||
self.object_to_element(item, subelement)
|
||||
elif isinstance(obj, dict):
|
||||
for key, value in obj.items():
|
||||
subelement = etree.SubElement(element, key)
|
||||
if key in JSON_ONLY_KEYS:
|
||||
if value:
|
||||
# Need to use json.dumps for the JSON inside XML
|
||||
# otherwise quotes get mangled and json.loads breaks
|
||||
try:
|
||||
subelement.text = json.dumps(value)
|
||||
except TypeError:
|
||||
subelement.text = str(value)
|
||||
else:
|
||||
self.object_to_element(value, subelement)
|
||||
else:
|
||||
element.text = str(obj)
|
||||
|
||||
def to_xml(self, data):
|
||||
# Assumption : root node is dict with single key
|
||||
root = data.keys()[0]
|
||||
eltree = etree.Element(root)
|
||||
self.object_to_element(data.get(root), eltree)
|
||||
response = etree.tostring(eltree)
|
||||
logger.debug("XML response : %s" % response)
|
||||
return response
|
||||
|
||||
def default(self, response, result):
|
||||
response.content_type = 'application/xml'
|
||||
response.body = self.to_xml(result)
|
|
@ -20,7 +20,6 @@
|
|||
Utility methods for working with WSGI servers
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import errno
|
||||
import json
|
||||
import logging
|
||||
|
@ -34,7 +33,6 @@ from eventlet.green import socket
|
|||
from eventlet.green import ssl
|
||||
import eventlet.greenio
|
||||
import eventlet.wsgi
|
||||
from lxml import etree
|
||||
from oslo.config import cfg
|
||||
from paste import deploy
|
||||
import routes
|
||||
|
@ -43,6 +41,7 @@ import webob.dec
|
|||
import webob.exc
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common import serializers
|
||||
from heat.openstack.common import gettextutils
|
||||
from heat.openstack.common import importutils
|
||||
|
||||
|
@ -565,65 +564,6 @@ class JSONRequestDeserializer(object):
|
|||
return {}
|
||||
|
||||
|
||||
class JSONResponseSerializer(object):
|
||||
|
||||
def to_json(self, data):
|
||||
def sanitizer(obj):
|
||||
if isinstance(obj, datetime.datetime):
|
||||
return obj.isoformat()
|
||||
return obj
|
||||
|
||||
response = json.dumps(data, default=sanitizer)
|
||||
logging.debug("JSON response : %s" % response)
|
||||
return response
|
||||
|
||||
def default(self, response, result):
|
||||
response.content_type = 'application/json'
|
||||
response.body = self.to_json(result)
|
||||
|
||||
|
||||
# Escape XML serialization for these keys, as the AWS API defines them as
|
||||
# JSON inside XML when the response format is XML.
|
||||
JSON_ONLY_KEYS = ('TemplateBody', 'Metadata')
|
||||
|
||||
|
||||
class XMLResponseSerializer(object):
|
||||
|
||||
def object_to_element(self, obj, element):
|
||||
if isinstance(obj, list):
|
||||
for item in obj:
|
||||
subelement = etree.SubElement(element, "member")
|
||||
self.object_to_element(item, subelement)
|
||||
elif isinstance(obj, dict):
|
||||
for key, value in obj.items():
|
||||
subelement = etree.SubElement(element, key)
|
||||
if key in JSON_ONLY_KEYS:
|
||||
if value:
|
||||
# Need to use json.dumps for the JSON inside XML
|
||||
# otherwise quotes get mangled and json.loads breaks
|
||||
try:
|
||||
subelement.text = json.dumps(value)
|
||||
except TypeError:
|
||||
subelement.text = str(value)
|
||||
else:
|
||||
self.object_to_element(value, subelement)
|
||||
else:
|
||||
element.text = str(obj)
|
||||
|
||||
def to_xml(self, data):
|
||||
# Assumption : root node is dict with single key
|
||||
root = data.keys()[0]
|
||||
eltree = etree.Element(root)
|
||||
self.object_to_element(data.get(root), eltree)
|
||||
response = etree.tostring(eltree)
|
||||
logging.debug("XML response : %s" % response)
|
||||
return response
|
||||
|
||||
def default(self, response, result):
|
||||
response.content_type = 'application/xml'
|
||||
response.body = self.to_xml(result)
|
||||
|
||||
|
||||
class Resource(object):
|
||||
"""
|
||||
WSGI app that handles (de)serialization and controller dispatch.
|
||||
|
@ -708,9 +648,9 @@ class Resource(object):
|
|||
serializer = self.serializer
|
||||
if serializer is None:
|
||||
if content_type == "JSON":
|
||||
serializer = JSONResponseSerializer()
|
||||
serializer = serializers.JSONResponseSerializer()
|
||||
else:
|
||||
serializer = XMLResponseSerializer()
|
||||
serializer = serializers.XMLResponseSerializer()
|
||||
|
||||
response = webob.Response(request=request)
|
||||
self.dispatch(serializer, action, response, action_result)
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
#
|
||||
# Copyright 2010-2011 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 datetime
|
||||
import webob
|
||||
|
||||
from heat.common import serializers
|
||||
from heat.tests.common import HeatTestCase
|
||||
|
||||
|
||||
class JSONResponseSerializerTest(HeatTestCase):
|
||||
|
||||
def test_to_json(self):
|
||||
fixture = {"key": "value"}
|
||||
expected = '{"key": "value"}'
|
||||
actual = serializers.JSONResponseSerializer().to_json(fixture)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_to_json_with_date_format_value(self):
|
||||
fixture = {"date": datetime.datetime(1, 3, 8, 2)}
|
||||
expected = '{"date": "0001-03-08T02:00:00"}'
|
||||
actual = serializers.JSONResponseSerializer().to_json(fixture)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_to_json_with_more_deep_format(self):
|
||||
fixture = {"is_public": True, "name": [{"name1": "test"}]}
|
||||
expected = '{"is_public": true, "name": [{"name1": "test"}]}'
|
||||
actual = serializers.JSONResponseSerializer().to_json(fixture)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_default(self):
|
||||
fixture = {"key": "value"}
|
||||
response = webob.Response()
|
||||
serializers.JSONResponseSerializer().default(response, fixture)
|
||||
self.assertEqual(200, response.status_int)
|
||||
content_types = filter(lambda h: h[0] == 'Content-Type',
|
||||
response.headerlist)
|
||||
self.assertEqual(1, len(content_types))
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual('{"key": "value"}', response.body)
|
||||
|
||||
|
||||
class XMLResponseSerializerTest(HeatTestCase):
|
||||
|
||||
def test_to_xml(self):
|
||||
fixture = {"key": "value"}
|
||||
expected = '<key>value</key>'
|
||||
actual = serializers.XMLResponseSerializer().to_xml(fixture)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_to_xml_with_date_format_value(self):
|
||||
fixture = {"date": datetime.datetime(1, 3, 8, 2)}
|
||||
expected = '<date>0001-03-08 02:00:00</date>'
|
||||
actual = serializers.XMLResponseSerializer().to_xml(fixture)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_to_xml_with_list(self):
|
||||
fixture = {"name": ["1", "2"]}
|
||||
expected = '<name><member>1</member><member>2</member></name>'
|
||||
actual = serializers.XMLResponseSerializer().to_xml(fixture)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_to_xml_with_more_deep_format(self):
|
||||
# Note we expect tree traversal from one root key, which is compatible
|
||||
# with the AWS format responses we need to serialize
|
||||
fixture = {"aresponse":
|
||||
{"is_public": True, "name": [{"name1": "test"}]}}
|
||||
expected = ('<aresponse><is_public>True</is_public>'
|
||||
'<name><member><name1>test</name1></member></name>'
|
||||
'</aresponse>')
|
||||
actual = serializers.XMLResponseSerializer().to_xml(fixture)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_to_xml_with_json_only_keys(self):
|
||||
# Certain keys are excluded from serialization because CFN
|
||||
# format demands a json blob in the XML body
|
||||
fixture = {"aresponse":
|
||||
{"is_public": True,
|
||||
"TemplateBody": {"name1": "test"},
|
||||
"Metadata": {"name2": "test2"}}}
|
||||
expected = ('<aresponse><is_public>True</is_public>'
|
||||
'<TemplateBody>{"name1": "test"}</TemplateBody>'
|
||||
'<Metadata>{"name2": "test2"}</Metadata></aresponse>')
|
||||
actual = serializers.XMLResponseSerializer().to_xml(fixture)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_default(self):
|
||||
fixture = {"key": "value"}
|
||||
response = webob.Response()
|
||||
serializers.XMLResponseSerializer().default(response, fixture)
|
||||
self.assertEqual(200, response.status_int)
|
||||
content_types = filter(lambda h: h[0] == 'Content-Type',
|
||||
response.headerlist)
|
||||
self.assertEqual(1, len(content_types))
|
||||
self.assertEqual('application/xml', response.content_type)
|
||||
self.assertEqual('<key>value</key>', response.body)
|
|
@ -15,7 +15,6 @@
|
|||
# under the License.
|
||||
|
||||
|
||||
import datetime
|
||||
import json
|
||||
from oslo.config import cfg
|
||||
import stubout
|
||||
|
@ -258,94 +257,6 @@ class ResourceExceptionHandlingTest(HeatTestCase):
|
|||
self.assertNotIn(str(e), self.logger.output)
|
||||
|
||||
|
||||
class JSONResponseSerializerTest(HeatTestCase):
|
||||
|
||||
def test_to_json(self):
|
||||
fixture = {"key": "value"}
|
||||
expected = '{"key": "value"}'
|
||||
actual = wsgi.JSONResponseSerializer().to_json(fixture)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_to_json_with_date_format_value(self):
|
||||
fixture = {"date": datetime.datetime(1, 3, 8, 2)}
|
||||
expected = '{"date": "0001-03-08T02:00:00"}'
|
||||
actual = wsgi.JSONResponseSerializer().to_json(fixture)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_to_json_with_more_deep_format(self):
|
||||
fixture = {"is_public": True, "name": [{"name1": "test"}]}
|
||||
expected = '{"is_public": true, "name": [{"name1": "test"}]}'
|
||||
actual = wsgi.JSONResponseSerializer().to_json(fixture)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_default(self):
|
||||
fixture = {"key": "value"}
|
||||
response = webob.Response()
|
||||
wsgi.JSONResponseSerializer().default(response, fixture)
|
||||
self.assertEqual(200, response.status_int)
|
||||
content_types = filter(lambda h: h[0] == 'Content-Type',
|
||||
response.headerlist)
|
||||
self.assertEqual(1, len(content_types))
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual('{"key": "value"}', response.body)
|
||||
|
||||
|
||||
class XMLResponseSerializerTest(HeatTestCase):
|
||||
|
||||
def test_to_xml(self):
|
||||
fixture = {"key": "value"}
|
||||
expected = '<key>value</key>'
|
||||
actual = wsgi.XMLResponseSerializer().to_xml(fixture)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_to_xml_with_date_format_value(self):
|
||||
fixture = {"date": datetime.datetime(1, 3, 8, 2)}
|
||||
expected = '<date>0001-03-08 02:00:00</date>'
|
||||
actual = wsgi.XMLResponseSerializer().to_xml(fixture)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_to_xml_with_list(self):
|
||||
fixture = {"name": ["1", "2"]}
|
||||
expected = '<name><member>1</member><member>2</member></name>'
|
||||
actual = wsgi.XMLResponseSerializer().to_xml(fixture)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_to_xml_with_more_deep_format(self):
|
||||
# Note we expect tree traversal from one root key, which is compatible
|
||||
# with the AWS format responses we need to serialize
|
||||
fixture = {"aresponse":
|
||||
{"is_public": True, "name": [{"name1": "test"}]}}
|
||||
expected = ('<aresponse><is_public>True</is_public>'
|
||||
'<name><member><name1>test</name1></member></name>'
|
||||
'</aresponse>')
|
||||
actual = wsgi.XMLResponseSerializer().to_xml(fixture)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_to_xml_with_json_only_keys(self):
|
||||
# Certain keys are excluded from serialization because CFN
|
||||
# format demands a json blob in the XML body
|
||||
fixture = {"aresponse":
|
||||
{"is_public": True,
|
||||
"TemplateBody": {"name1": "test"},
|
||||
"Metadata": {"name2": "test2"}}}
|
||||
expected = ('<aresponse><is_public>True</is_public>'
|
||||
'<TemplateBody>{"name1": "test"}</TemplateBody>'
|
||||
'<Metadata>{"name2": "test2"}</Metadata></aresponse>')
|
||||
actual = wsgi.XMLResponseSerializer().to_xml(fixture)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_default(self):
|
||||
fixture = {"key": "value"}
|
||||
response = webob.Response()
|
||||
wsgi.XMLResponseSerializer().default(response, fixture)
|
||||
self.assertEqual(200, response.status_int)
|
||||
content_types = filter(lambda h: h[0] == 'Content-Type',
|
||||
response.headerlist)
|
||||
self.assertEqual(1, len(content_types))
|
||||
self.assertEqual('application/xml', response.content_type)
|
||||
self.assertEqual('<key>value</key>', response.body)
|
||||
|
||||
|
||||
class JSONRequestDeserializerTest(HeatTestCase):
|
||||
|
||||
def test_has_body_no_content_length(self):
|
||||
|
|
Loading…
Reference in New Issue