Add limit query filter param
This PS adds a limit query filter parameter to allow users to limit the number of documents returned by revision documents as well as rendered documents. Change-Id: Ic15dc59cd21d82be552fa7b9885754bde47724a0
This commit is contained in:
parent
97578a933f
commit
93a3274425
@ -14,6 +14,8 @@
|
||||
|
||||
import functools
|
||||
|
||||
import falcon
|
||||
|
||||
|
||||
class ViewBuilder(object):
|
||||
"""Model API responses as dictionaries."""
|
||||
@ -51,6 +53,24 @@ def sanitize_params(allowed_params):
|
||||
def wrapper(self, req, *func_args, **func_kwargs):
|
||||
req_params = req.params or {}
|
||||
sanitized_params = {}
|
||||
# This maps which type should be enforced per query parameter.
|
||||
# Everything not included in type dict below is assumed to be a
|
||||
# string or a list of strings.
|
||||
type_dict = {'limit': int}
|
||||
|
||||
def _enforce_query_filter_type(key, val):
|
||||
if key in type_dict:
|
||||
cast_type = type_dict[key]
|
||||
try:
|
||||
cast_val = cast_type(val)
|
||||
except Exception:
|
||||
raise falcon.HTTPInvalidParam(
|
||||
'Query parameter %s must be of type %s.' % (
|
||||
key, cast_type),
|
||||
key)
|
||||
return cast_val
|
||||
else:
|
||||
return val
|
||||
|
||||
def _convert_to_dict(sanitized_params, filter_key, filter_val):
|
||||
# Key-value pairs like metadata.label=foo=bar need to be
|
||||
@ -68,21 +88,25 @@ def sanitize_params(allowed_params):
|
||||
pass
|
||||
|
||||
for key, val in req_params.items():
|
||||
param_val = _enforce_query_filter_type(key, val)
|
||||
|
||||
if not isinstance(val, list):
|
||||
val = [val]
|
||||
is_key_val_pair = '=' in val[0]
|
||||
|
||||
is_key_val_pair = isinstance(val, list) and '=' in val[0]
|
||||
|
||||
if key in allowed_params:
|
||||
if key in _mapping:
|
||||
if is_key_val_pair:
|
||||
_convert_to_dict(
|
||||
sanitized_params, _mapping[key], val)
|
||||
else:
|
||||
sanitized_params[_mapping[key]] = req_params[key]
|
||||
sanitized_params[_mapping[key]] = param_val
|
||||
else:
|
||||
if is_key_val_pair:
|
||||
_convert_to_dict(sanitized_params, key, val)
|
||||
else:
|
||||
sanitized_params[key] = req_params[key]
|
||||
sanitized_params[key] = param_val
|
||||
|
||||
func_args = func_args + (sanitized_params,)
|
||||
return func(self, req, *func_args, **func_kwargs)
|
||||
|
@ -41,7 +41,7 @@ class RevisionDocumentsResource(api_base.BaseResource):
|
||||
@common.sanitize_params([
|
||||
'schema', 'metadata.name', 'metadata.layeringDefinition.abstract',
|
||||
'metadata.layeringDefinition.layer', 'metadata.label',
|
||||
'status.bucket', 'order', 'sort'])
|
||||
'status.bucket', 'order', 'sort', 'limit'])
|
||||
def on_get(self, req, resp, sanitized_params, revision_id):
|
||||
"""Returns all documents for a `revision_id`.
|
||||
|
||||
@ -55,6 +55,7 @@ class RevisionDocumentsResource(api_base.BaseResource):
|
||||
|
||||
order_by = sanitized_params.pop('order', None)
|
||||
sort_by = sanitized_params.pop('sort', None)
|
||||
limit = sanitized_params.pop('limit', None)
|
||||
|
||||
filters = sanitized_params.copy()
|
||||
filters['metadata.storagePolicy'] = ['cleartext']
|
||||
@ -71,6 +72,8 @@ class RevisionDocumentsResource(api_base.BaseResource):
|
||||
|
||||
# Sorts by creation date by default.
|
||||
documents = utils.multisort(documents, sort_by, order_by)
|
||||
if limit is not None:
|
||||
documents = documents[:limit]
|
||||
|
||||
resp.status = falcon.HTTP_200
|
||||
resp.body = self.view_builder.list(documents)
|
||||
@ -95,7 +98,7 @@ class RenderedDocumentsResource(api_base.BaseResource):
|
||||
@policy.authorize('deckhand:list_cleartext_documents')
|
||||
@common.sanitize_params([
|
||||
'schema', 'metadata.name', 'metadata.layeringDefinition.layer',
|
||||
'metadata.label', 'status.bucket', 'order', 'sort'])
|
||||
'metadata.label', 'status.bucket', 'order', 'sort', 'limit'])
|
||||
def on_get(self, req, resp, sanitized_params, revision_id):
|
||||
include_encrypted = policy.conditional_authorize(
|
||||
'deckhand:list_encrypted_documents', req.context, do_raise=False)
|
||||
@ -135,6 +138,7 @@ class RenderedDocumentsResource(api_base.BaseResource):
|
||||
# returns concrete documents, so no filtering for that is needed here.
|
||||
order_by = sanitized_params.pop('order', None)
|
||||
sort_by = sanitized_params.pop('sort', None)
|
||||
limit = sanitized_params.pop('limit', None)
|
||||
user_filters = sanitized_params.copy()
|
||||
|
||||
rendered_documents = [
|
||||
@ -145,6 +149,9 @@ class RenderedDocumentsResource(api_base.BaseResource):
|
||||
rendered_documents = utils.multisort(
|
||||
rendered_documents, sort_by, order_by)
|
||||
|
||||
if limit is not None:
|
||||
rendered_documents = rendered_documents[:limit]
|
||||
|
||||
resp.status = falcon.HTTP_200
|
||||
resp.body = self.view_builder.list(rendered_documents)
|
||||
self._post_validate(rendered_documents)
|
||||
|
@ -46,3 +46,10 @@ tests:
|
||||
schema: example/Kind/v2
|
||||
status: 200
|
||||
response_multidoc_jsonpaths: null
|
||||
|
||||
- name: filter_by_limit_illegal_value
|
||||
desc: Verify that illegal limit value returns 400
|
||||
GET: /api/v1.0/revisions/$HISTORY['initialize'].$RESPONSE['$.[0].status.revision']/documents
|
||||
query_parameters:
|
||||
limit: 'illegal'
|
||||
status: 400
|
||||
|
@ -208,3 +208,21 @@ tests:
|
||||
- deckhand/LayeringPolicy/v1
|
||||
- example/Kind/v1
|
||||
- example/Kind/v1
|
||||
|
||||
- name: limit_by_permitted_int_value
|
||||
desc: Verify revision documents limited by int value
|
||||
GET: /api/v1.0/revisions/$RESPONSE['$.[0].status.revision']/documents
|
||||
query_parameters:
|
||||
sort:
|
||||
- metadata.name
|
||||
- schema
|
||||
limit: 2
|
||||
status: 200
|
||||
response_multidoc_jsonpaths:
|
||||
$.`len`: 2
|
||||
$.[*].metadata.name:
|
||||
- global-1234
|
||||
- layering-policy
|
||||
$.[*].schema:
|
||||
- example/Kind/v1
|
||||
- deckhand/LayeringPolicy/v1
|
||||
|
@ -276,3 +276,43 @@ class TestRevisionDocumentsControllerSorting(test_base.BaseControllerTest):
|
||||
self.assertEqual(3, len(retrieved_documents))
|
||||
self.assertEqual(expected_schemas,
|
||||
[d['schema'] for d in retrieved_documents])
|
||||
|
||||
def test_list_revision_documents_sorting_by_schema_then_limit(self):
|
||||
rules = {'deckhand:list_cleartext_documents': '@',
|
||||
'deckhand:list_encrypted_documents': '@',
|
||||
'deckhand:create_cleartext_documents': '@'}
|
||||
self.policy.set_rules(rules)
|
||||
|
||||
documents_factory = factories.DocumentFactory(2, [1, 1])
|
||||
documents = documents_factory.gen_test({
|
||||
'_SITE_ACTIONS_1_': {
|
||||
'actions': [{'method': 'merge', 'path': '.'}]
|
||||
}
|
||||
})
|
||||
schemas = ['deckhand/Certificate/v1',
|
||||
'deckhand/CertificateKey/v1',
|
||||
'deckhand/LayeringPolicy/v1']
|
||||
for idx in range(len(documents)):
|
||||
documents[idx]['schema'] = schemas[idx]
|
||||
|
||||
for limit in (0, 1, 2, 3):
|
||||
expected_schemas = schemas[:limit]
|
||||
|
||||
resp = self.app.simulate_put(
|
||||
'/api/v1.0/buckets/mop/documents',
|
||||
headers={'Content-Type': 'application/x-yaml'},
|
||||
body=yaml.safe_dump_all(documents))
|
||||
self.assertEqual(200, resp.status_code)
|
||||
revision_id = list(yaml.safe_load_all(resp.text))[0]['status'][
|
||||
'revision']
|
||||
|
||||
resp = self.app.simulate_get(
|
||||
'/api/v1.0/revisions/%s/documents' % revision_id,
|
||||
params={'sort': 'schema', 'limit': limit}, params_csv=False,
|
||||
headers={'Content-Type': 'application/x-yaml'})
|
||||
self.assertEqual(200, resp.status_code)
|
||||
retrieved_documents = list(yaml.safe_load_all(resp.text))
|
||||
|
||||
self.assertEqual(limit, len(retrieved_documents))
|
||||
self.assertEqual(expected_schemas,
|
||||
[d['schema'] for d in retrieved_documents])
|
||||
|
@ -86,6 +86,7 @@ Supported query string parameters:
|
||||
"asc". Controls the order in which the ``sort`` result is returned: "asc"
|
||||
returns sorted results in ascending order, while "desc" returns results in
|
||||
descending order.
|
||||
* ``limit`` - int - Controls number of documents returned by this endpoint.
|
||||
|
||||
GET ``/revisions/{revision_id}/rendered-documents``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -96,7 +97,7 @@ consumers will interact with for their configuration.
|
||||
|
||||
Valid query parameters are the same as for
|
||||
``/revisions/{revision_id}/documents``, minus the parameters in
|
||||
``metadata.layeringDetinition``, which are not supported.
|
||||
``metadata.layeringDefinition``, which are not supported.
|
||||
|
||||
Raises a ``409 Conflict`` if a ``layeringPolicy`` document could not be found.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user