webapp: use content detection for return

Rather than having end-points with ".json", check the accept-header
and return the correct thing based on that.

Change-Id: Ia0e4cb90cdaa113bb1bf7b4636bc10293811f0f6
This commit is contained in:
Ian Wienand 2018-02-28 14:01:16 +11:00
parent 89790013f3
commit 2dcd79b987
3 changed files with 55 additions and 49 deletions

View File

@ -232,26 +232,18 @@ launchers, all will provide the same information.
The status of uploaded images
:query fields: comma-separated list of fields to display
:resheader Content-Type: text/plain
.. http:get:: /image-list.json
The status of uploaded images
:resheader Content-Type: application/json
:reqheader Accept: ``application/json`` or ``text/*``
:resheader Content-Type: ``application/json`` or ``text/plain``
depending on the :http:header:`Accept` header
.. http:get:: /dib-image-list
The status of images built by ``diskimage-builder``
:query fields: comma-separated list of fields to display
:resheader Content-Type: text/plain
.. http:get:: /dib-image-list.json
The status of images built by ``diskimage-builder``
:resheader Content-Type: application/json
:reqheader Accept: ``application/json`` or ``text/*``
:resheader Content-Type: ``application/json`` or ``text/plain``
depending on the :http:header:`Accept` header
.. http:get:: /node-list
@ -259,24 +251,15 @@ launchers, all will provide the same information.
:query node_id: restrict to a specific node
:query fields: comma-separated list of fields to display
:resheader Content-Type: text/plain
.. http:get:: /node-list.json
The status of currently active nodes
:query node_id: restrict to a specific node
:resheader Content-Type: application/json
:reqheader Accept: ``application/json`` or ``text/*``
:resheader Content-Type: ``application/json`` or ``text/plain``
depending on the :http:header:`Accept` header
.. http:get:: /request-list
Outstanding requests
:query fields: comma-separated list of fields to display
:resheader Content-Type: text/plain
.. http:get:: /request-list.json
Outstanding requests
:resheader Content-Type: application/json
:reqheader Accept: ``application/json`` or ``text/*``
:resheader Content-Type: ``application/json`` or ``text/plain``
depending on the :http:header:`Accept` header

View File

@ -39,6 +39,9 @@ class TestWebApp(tests.DBTestCase):
req = request.Request(
"http://localhost:%s/image-list" % port)
# NOTE(ianw): we want pretty printed text/plain back, but
# simulating a normal web-browser request.
req.add_header('Accept', 'text/html')
f = request.urlopen(req)
self.assertEqual(f.info().get('Content-Type'),
'text/plain; charset=UTF-8')
@ -59,6 +62,7 @@ class TestWebApp(tests.DBTestCase):
req = request.Request(
"http://localhost:%s/image-list?fields=id,image,state" % port)
req.add_header('Accept', 'text/html')
f = request.urlopen(req)
self.assertEqual(f.info().get('Content-Type'),
'text/plain; charset=UTF-8')
@ -79,7 +83,8 @@ class TestWebApp(tests.DBTestCase):
self.waitForNodes('fake-label')
req = request.Request(
"http://localhost:%s/image-list.json" % port)
"http://localhost:%s/image-list" % port)
req.add_header('Accept', 'application/json')
f = request.urlopen(req)
self.assertEqual(f.info().get('Content-Type'),
'application/json')
@ -103,7 +108,8 @@ class TestWebApp(tests.DBTestCase):
self.waitForNodes('fake-label')
req = request.Request(
"http://localhost:%s/dib-image-list.json" % port)
"http://localhost:%s/dib-image-list" % port)
req.add_header('Accept', 'application/json')
f = request.urlopen(req)
self.assertEqual(f.info().get('Content-Type'),
'application/json')
@ -128,7 +134,8 @@ class TestWebApp(tests.DBTestCase):
self.waitForNodes('fake-label')
req = request.Request(
"http://localhost:%s/node-list.json" % port)
"http://localhost:%s/node-list" % port)
req.add_header('Accept', 'application/json')
f = request.urlopen(req)
self.assertEqual(f.info().get('Content-Type'),
'application/json')
@ -143,9 +150,10 @@ class TestWebApp(tests.DBTestCase):
'state': 'ready'}, objs[0])
# specify valid node_id
req = request.Request(
"http://localhost:%s/node-list.json?node_id=%s" % (port,
'0000000000'))
"http://localhost:%s/node-list?node_id=%s" % (port,
'0000000000'))
f = request.urlopen(req)
req.add_header('Accept', 'application/json')
self.assertEqual(f.info().get('Content-Type'),
'application/json')
data = f.read()
@ -159,8 +167,9 @@ class TestWebApp(tests.DBTestCase):
'state': 'ready'}, objs[0])
# node_id not found
req = request.Request(
"http://localhost:%s/node-list.json?node_id=%s" % (port,
'999999'))
"http://localhost:%s/node-list?node_id=%s" % (port,
'999999'))
req.add_header('Accept', 'application/json')
f = request.urlopen(req)
self.assertEqual(f.info().get('Content-Type'),
'application/json')
@ -186,7 +195,8 @@ class TestWebApp(tests.DBTestCase):
self.zk.storeNodeRequest(req)
http_req = request.Request(
"http://localhost:%s/request-list.json" % port)
"http://localhost:%s/request-list" % port)
http_req.add_header('Accept', 'application/json')
f = request.urlopen(http_req)
self.assertEqual(f.info().get('Content-Type'),
'application/json')

View File

@ -74,23 +74,20 @@ class WebApp(threading.Thread):
def stop(self):
self.server.server_close()
def get_cache(self, path, params):
def get_cache(self, path, params, request_type):
# TODO quick and dirty way to take query parameters
# into account when caching data
if params:
index = path + json.dumps(params.dict_of_lists(), sort_keys=True)
index = "%s.%s.%s" % (path,
json.dumps(params.dict_of_lists(),
sort_keys=True),
request_type)
else:
index = path
index = "%s.%s" % (path, request_type)
result = self.cache.get(index)
if result:
return result
if path.endswith('.json'):
out_fmt = 'json'
path = path[:-5]
else:
out_fmt = 'pretty'
zk = self.nodepool.getZK()
if path == '/image-list':
@ -109,16 +106,32 @@ class WebApp(threading.Thread):
if params.get('fields'):
fields = params.get('fields').split(',')
output = status.output(results, out_fmt, fields)
output = status.output(results, request_type, fields)
return self.cache.put(index, output)
def _request_wants(self, request):
'''Find request content-type
:param request: The incoming request
:return str: Best guess of either 'pretty' or 'json'
'''
best = request.accept.best_match(
['application/json', 'text/plain'])
if best == 'application/json':
return 'json'
else:
return 'pretty'
def app(self, request):
result = self.get_cache(request.path, request.params)
request_type = self._request_wants(request)
result = self.get_cache(request.path, request.params,
request_type)
if result is None:
raise webob.exc.HTTPNotFound()
last_modified, output = result
if request.path.endswith('.json'):
if request_type == 'json':
content_type = 'application/json'
else:
content_type = 'text/plain'