Skip to content

Commit

Permalink
Merge f75d2d6 into 0b92038
Browse files Browse the repository at this point in the history
  • Loading branch information
alvarolopez committed Apr 10, 2015
2 parents 0b92038 + f75d2d6 commit 701fbcd
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 57 deletions.
2 changes: 1 addition & 1 deletion ooi/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def scheme_occurrences():
if len(s) != 0:
mismatched_schemas = [(scheme, d[scheme])
for scheme in dict(s).keys()]
raise exception.OCCISchemaOcurrencesMismatch(
raise exception.OCCISchemaOccurrencesMismatch(
mismatched_schemas=mismatched_schemas)

def term_validation():
Expand Down
2 changes: 1 addition & 1 deletion ooi/api/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def get_from_response(response, element, default):
:param element: The element to look for in the JSON body
:param default: The default element to be returned if not found.
"""
if response.status_int == 200:
if response.status_int in [200, 201, 202]:
return response.json_body.get(element, default)
else:
raise exception_from_response(response)
Expand Down
10 changes: 9 additions & 1 deletion ooi/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,18 @@ class Invalid(OCCIException):


class InvalidContentType(Invalid):
msg_fmt = "Invalid content type %(content_type)s."
msg_fmt = "Invalid Content-type %(content_type)s."
code = 406


class NoContentType(InvalidContentType):
msg_fmt = "No Content-type provided."


class InvalidAccept(InvalidContentType):
msg_fmt = "Invalid Accept %(content_type)s."


class NotImplemented(OCCIException):
msg_fmt = "Action not implemented."
code = 501
Expand Down
2 changes: 1 addition & 1 deletion ooi/occi/rendering/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def render(self, env={}):

class ExceptionRenderer(HeaderRenderer):
def render(self, env={}):
return []
return [("X-OCCI-Error", self.obj.explanation)]


class CategoryRenderer(HeaderRenderer):
Expand Down
20 changes: 20 additions & 0 deletions ooi/tests/middleware/test_compute_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,26 @@ def test_create_vm(self):
self.assertExpectedResult(expected, resp)
self.assertContentType(resp)

def test_create_vm_incomplete(self):
tenant = fakes.tenants["foo"]

app = self.get_app()
headers = {
'Category': (
'compute;'
'scheme="http://schemas.ogf.org/occi/infrastructure#";'
'class="kind",'
'bar;'
'scheme="http://schemas.openstack.org/template/os#";'
'class="mixin"')
}
req = self._build_req("/compute", tenant["id"], method="POST",
headers=headers)
resp = req.get_response(app)

self.assertEqual(400, resp.status_code)
self.assertContentType(resp)


class ComputeControllerTextPlain(test_middleware.TestMiddlewareTextPlain,
TestComputeController):
Expand Down
10 changes: 8 additions & 2 deletions ooi/tests/middleware/test_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,17 @@ class TestMiddleware(base.TestCase):
def setUp(self):
super(TestMiddleware, self).setUp()

self.accept = None
self.accept = self.content_type = None
self.application_url = fakes.application_url

def get_app(self, resp=None):
return wsgi.OCCIMiddleware(fakes.FakeApp())

def assertContentType(self, result):
expected = self.accept or "text/plain"
if self.accept in (None, "*/*"):
expected = "text/plain"
else:
expected = self.accept
self.assertEqual(expected, result.content_type)

def assertExpectedResult(self, expected, result):
Expand All @@ -54,6 +57,9 @@ def _build_req(self, path, tenant_id, **kwargs):
if self.accept is not None:
kwargs["accept"] = self.accept

if self.content_type is not None:
kwargs["content_type"] = self.content_type

m = mock.MagicMock()
m.user.project_id = tenant_id
environ = {"keystone.token_auth": m}
Expand Down
29 changes: 25 additions & 4 deletions ooi/tests/test_wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,12 @@

@webob.dec.wsgify
def fake_app(req):
resp = webob.Response("Hi")
resp = webob.Response("Foo")
return resp


class FakeController(object):
def index(self, *args, **kwargs):
# Return none so that the middleware passes to the app
return None

def create(self, req, body):
Expand Down Expand Up @@ -63,7 +62,7 @@ def test_index(self):
result = webob.Request.blank("/foos",
method="GET").get_response(self.app)
self.assertEqual(200, result.status_code)
self.assertEqual("Hi", result.text)
self.assertEqual("", result.text)

def test_show(self):
result = webob.Request.blank("/foos/stop",
Expand Down Expand Up @@ -91,10 +90,32 @@ def test_404(self):
result = webob.Request.blank("/bazonk").get_response(self.app)
self.assertEqual(404, result.status_code)

def test_empty_accept(self):
req = webob.Request.blank("/foos",
method="GET",
accept=None)
result = req.get_response(self.app)
self.assertEqual(200, result.status_code)
self.assertEqual("text/plain", result.content_type)

def test_accept_all(self):
req = webob.Request.blank("/foos",
method="GET",
accept="*/*")
result = req.get_response(self.app)
self.assertEqual(200, result.status_code)
self.assertEqual("text/plain", result.content_type)

def test_bad_accept(self):
req = webob.Request.blank("/foos",
method="GET",
accept="foo/bazonk",
accept="foo/bazonk")
result = req.get_response(self.app)
self.assertEqual(406, result.status_code)

def test_bad_content_type(self):
req = webob.Request.blank("/foos",
method="GET",
content_type="foo/bazonk")
result = req.get_response(self.app)
self.assertEqual(406, result.status_code)
Expand Down
88 changes: 42 additions & 46 deletions ooi/wsgi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import ooi.api.compute
from ooi.api import query
from ooi import exception
from ooi.occi.core import collection
from ooi import utils
from ooi.wsgi import serializers

Expand All @@ -31,30 +30,25 @@

class Request(webob.Request):
def get_content_type(self):
"""Determine content type of the request body.
Does not do any body introspection, only checks header
"""
if "Content-Type" not in self.headers:
return None

content_type = self.content_type

if not content_type or content_type == 'text/plain':
"""Determine content type of the request body."""
if not self.content_type:
return None

if content_type not in serializers.get_supported_content_types():
raise exception.InvalidContentType(content_type=content_type)
# FIXME: we should change this, since the content type does not depend
# on the serializers, but on the parsers
if self.content_type not in serializers.get_supported_content_types():
LOG.debug("Unrecognized Content-Type provided in request")
raise exception.InvalidContentType(content_type=self.content_type)

return content_type
return self.content_type

def get_best_match_content_type(self):
content_type = self.get_content_type()
if content_type is None:
content_types = serializers.get_supported_content_types()
content_type = self.accept.best_match(content_types,
default_match="text/plain")
def get_best_match_content_type(self, default_match=None):
content_types = serializers.get_supported_content_types()
content_type = self.accept.best_match(content_types,
default_match=default_match)
if not content_type:
LOG.debug("Unrecognized Accept Content-type provided in request")
raise exception.InvalidAccept(content_type=content_type)
return content_type


Expand Down Expand Up @@ -164,15 +158,6 @@ def get_action_args(self, args):

return args

def get_body(self, request):
try:
content_type = request.get_content_type()
except exception.InvalidContentType:
LOG.debug("Unrecognized Content-Type provided in request")
return None, ''

return content_type, request.body

@staticmethod
def _should_have_body(request):
return request.method in ("POST", "PUT")
Expand All @@ -183,11 +168,13 @@ def __call__(self, request, args):
action = action_args.pop('action', None)
try:
accept = request.get_best_match_content_type()
except exception.InvalidContentType:
msg = "Unsupported Content-Type"
content_type = request.get_content_type()
except exception.InvalidContentType as e:
msg = e.format_message()
return Fault(webob.exc.HTTPNotAcceptable(explanation=msg))

content_type, body = self.get_body(request)
body = request.body

# Get the implementing method
try:
method = self.get_method(request, action,
Expand Down Expand Up @@ -216,18 +203,17 @@ def __call__(self, request, args):
response = ex

# No exceptions, so create a response
# NOTE(aloga): if the middleware returns None, the pipeline will
# continue, but we do not want to do so, so we convert the action
# result to a ResponseObject.
if not response:
resp_obj = None
# We got something
if isinstance(action_result, (list, collection.Collection)):
resp_obj = ResponseObject(action_result)
elif isinstance(action_result, ResponseObject):
if isinstance(action_result, ResponseObject):
resp_obj = action_result
else:
response = action_result
if resp_obj and not response:
response = resp_obj.serialize(request, accept,
self.default_serializers)
resp_obj = ResponseObject(action_result)

response = resp_obj.serialize(request, accept,
self.default_serializers)
return response

def get_method(self, request, action, content_type, body):
Expand Down Expand Up @@ -293,8 +279,8 @@ def serialize(self, request, content_type, default_serializers=None):
for hdr, value in self._headers.items():
response.headers[hdr] = utils.utf8(value)
response.headers['Content-Type'] = content_type
response.charset = 'utf8'
if self.obj is not None:
response.charset = 'utf8'
headers, body = serializer.serialize(self.obj)
if headers is not None:
for hdr in headers:
Expand Down Expand Up @@ -366,7 +352,7 @@ def __init__(self, exception):
self.wrapped_exc.headers[key] = str(value)
self.status_int = exception.status_int

@webob.dec.wsgify()
@webob.dec.wsgify(RequestClass=Request)
def __call__(self, req):
"""Generate a WSGI response based on the exception passed to ctor."""

Expand All @@ -376,14 +362,24 @@ def __call__(self, req):
LOG.debug("Returning %(code)s to user: %(explanation)s",
{'code': code, 'explanation': explanation})

content_type = req.content_type or "text/plain"
def_ct = "text/plain"
content_type = req.get_best_match_content_type(default_match=def_ct)
mtype = serializers.get_media_map().get(content_type,
"text")
serializer = serializers.get_default_serializers()[mtype]
env = {}
serialized_exc = serializer(env).serialize(self.wrapped_exc)
self.wrapped_exc.body = serialized_exc[1]
self.wrapped_exc.content_type = content_type
self.wrapped_exc.body = serialized_exc[1]

# We need to specify the HEAD req.method here to be HEAD because of the
# way that webob.exc.WSGIHTTPException.__call__ generates the response.
# The text/occi will not have a body since it is based on headers. We
# cannot set this earlier in the middleware, since we are calling
# OpenStack and it will fail because the responses won't contain a
# body.
if content_type == "text/occi":
req.method = "HEAD"

return self.wrapped_exc

Expand Down
2 changes: 1 addition & 1 deletion ooi/wsgi/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def serialize(self, data):
# Header renderers will return a list, so we must flatten the results
# before returning them
headers = [i for r in renderers for i in r.render(env=self.env)]
return headers, ""
return headers, utils.utf8("")


_SERIALIZERS_MAP = {
Expand Down

0 comments on commit 701fbcd

Please sign in to comment.