From 6019b163d6b3ecbfef53a11314ebfca772552bc7 Mon Sep 17 00:00:00 2001 From: Brandon Logan Date: Sun, 12 Jun 2016 19:24:05 -0500 Subject: [PATCH] Pecan: move fields and filters logic to hooks The logic for massaging the fields and filters data was being done in the controllers. This change pulls that logic out of the controllers and puts it in a generic hook so that it's done for all controllers. This will later be used for pagination, but thats not the main motivation of this patch. Ideally, the filters and fields would be passed into the controller methods themselves as kwargs, but that is not easily done due to pecan not easily allowing overriding the args and kwargs passed to the controller methods. For now, the fields and filters will have to be pulled out of the request context. Change-Id: Ibea9087c7036faa064b87acb52e09c9a3c74148a --- neutron/pecan_wsgi/app.py | 1 + neutron/pecan_wsgi/controllers/resource.py | 18 ++---- neutron/pecan_wsgi/controllers/utils.py | 2 +- neutron/pecan_wsgi/hooks/__init__.py | 2 + neutron/pecan_wsgi/hooks/query_parameters.py | 59 +++++++++++++++++++ .../functional/pecan_wsgi/test_controllers.py | 7 ++- 6 files changed, 72 insertions(+), 17 deletions(-) create mode 100644 neutron/pecan_wsgi/hooks/query_parameters.py diff --git a/neutron/pecan_wsgi/app.py b/neutron/pecan_wsgi/app.py index 7fa2a0af47b..dcde26bdf44 100644 --- a/neutron/pecan_wsgi/app.py +++ b/neutron/pecan_wsgi/app.py @@ -51,6 +51,7 @@ def setup_app(*args, **kwargs): hooks.QuotaEnforcementHook(), # priority 130 hooks.NotifierHook(), # priority 135 hooks.PolicyHook(), # priority 140 + hooks.QueryParametersHook(), # priority 145 ] app = pecan.make_app( diff --git a/neutron/pecan_wsgi/controllers/resource.py b/neutron/pecan_wsgi/controllers/resource.py index c3ab68b504c..d5304d69891 100644 --- a/neutron/pecan_wsgi/controllers/resource.py +++ b/neutron/pecan_wsgi/controllers/resource.py @@ -17,7 +17,6 @@ import pecan from pecan import request from neutron._i18n import _LW -from neutron.api import api_common from neutron import manager from neutron.pecan_wsgi.controllers import utils @@ -39,7 +38,7 @@ class ItemController(utils.NeutronPecanController): def get(self, *args, **kwargs): getter = getattr(self.plugin, 'get_%s' % self.resource) neutron_context = request.context['neutron_context'] - fields = self._build_field_list(kwargs.pop('fields', [])) + fields = request.context['query_params'].get('fields') return {self.resource: getter(neutron_context, self.item, fields=fields)} @@ -99,18 +98,9 @@ class CollectionsController(utils.NeutronPecanController): return self.get(*args, **kwargs) def get(self, *args, **kwargs): - # list request - fields = kwargs.pop('fields', []) - # if only one fields query parameter is passed, pecan will not put - # that parameter in a list, so we need to convert it into a list - fields = fields if isinstance(fields, list) else [fields] - fields = self._build_field_list(fields) - _listify = lambda x: x if isinstance(x, list) else [x] - filters = api_common.get_filters_from_dict( - {k: _listify(v) for k, v in kwargs.items()}, - self.resource_info, - skips=['fields', 'sort_key', 'sort_dir', - 'limit', 'marker', 'page_reverse']) + # NOTE(blogan): these are set in the FieldsAndFiltersHoook + fields = request.context['query_params'].get('fields') + filters = request.context['query_params'].get('filters') lister = getattr(self.plugin, 'get_%s' % self.collection) neutron_context = request.context['neutron_context'] return {self.collection: lister(neutron_context, diff --git a/neutron/pecan_wsgi/controllers/utils.py b/neutron/pecan_wsgi/controllers/utils.py index 6ee19e3ea04..1ac042f7a83 100644 --- a/neutron/pecan_wsgi/controllers/utils.py +++ b/neutron/pecan_wsgi/controllers/utils.py @@ -102,7 +102,7 @@ class NeutronPecanController(object): else: self._mandatory_fields = set() - def _build_field_list(self, request_fields): + def build_field_list(self, request_fields): if request_fields: return set(request_fields) | self._mandatory_fields return [] diff --git a/neutron/pecan_wsgi/hooks/__init__.py b/neutron/pecan_wsgi/hooks/__init__.py index 899cccabe93..700b26dd92e 100644 --- a/neutron/pecan_wsgi/hooks/__init__.py +++ b/neutron/pecan_wsgi/hooks/__init__.py @@ -18,6 +18,7 @@ from neutron.pecan_wsgi.hooks import context from neutron.pecan_wsgi.hooks import notifier from neutron.pecan_wsgi.hooks import ownership_validation from neutron.pecan_wsgi.hooks import policy_enforcement +from neutron.pecan_wsgi.hooks import query_parameters from neutron.pecan_wsgi.hooks import quota_enforcement from neutron.pecan_wsgi.hooks import translation @@ -29,3 +30,4 @@ OwnershipValidationHook = ownership_validation.OwnershipValidationHook PolicyHook = policy_enforcement.PolicyHook QuotaEnforcementHook = quota_enforcement.QuotaEnforcementHook NotifierHook = notifier.NotifierHook +QueryParametersHook = query_parameters.QueryParametersHook diff --git a/neutron/pecan_wsgi/hooks/query_parameters.py b/neutron/pecan_wsgi/hooks/query_parameters.py new file mode 100644 index 00000000000..8815926721b --- /dev/null +++ b/neutron/pecan_wsgi/hooks/query_parameters.py @@ -0,0 +1,59 @@ +# 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. + +from pecan import hooks + +from neutron.api import api_common +from neutron import manager + + +def _listify(thing): + return thing if isinstance(thing, list) else [thing] + + +def _set_fields(state, controller): + params = state.request.params.mixed() + fields = params.get('fields', []) + # if only one fields query parameter is passed, pecan will not put + # that parameter in a list, so we need to convert it into a list + fields = _listify(fields) + combined_fields = controller.build_field_list(fields) + return combined_fields + + +def _set_filters(state, controller): + params = state.request.params.mixed() + filters = api_common.get_filters_from_dict( + {k: _listify(v) for k, v in params.items()}, + controller.resource_info, + skips=['fields', 'sort_key', 'sort_dir', + 'limit', 'marker', 'page_reverse']) + return filters + + +class QueryParametersHook(hooks.PecanHook): + + priority = 145 + + def before(self, state): + state.request.context['query_params'] = {} + if state.request.method != 'GET': + return + collection = state.request.context.get('collection') + if not collection: + return + controller = manager.NeutronManager.get_controller_for_resource( + collection) + combined_fields = _set_fields(state, controller) + filters = _set_filters(state, controller) + query_params = {'fields': combined_fields, 'filters': filters} + state.request.context['query_params'] = query_params diff --git a/neutron/tests/functional/pecan_wsgi/test_controllers.py b/neutron/tests/functional/pecan_wsgi/test_controllers.py index 2f083103d6b..0ca905d86b5 100644 --- a/neutron/tests/functional/pecan_wsgi/test_controllers.py +++ b/neutron/tests/functional/pecan_wsgi/test_controllers.py @@ -23,6 +23,7 @@ from neutron.api import extensions from neutron import context from neutron import manager from neutron.pecan_wsgi.controllers import root as controllers +from neutron.pecan_wsgi.controllers import utils as controller_utils from neutron.plugins.common import constants from neutron import policy from neutron.tests.common import helpers @@ -34,7 +35,7 @@ _SERVICE_PLUGIN_COLLECTION = _SERVICE_PLUGIN_RESOURCE + 's' _SERVICE_PLUGIN_INDEX_BODY = {_SERVICE_PLUGIN_COLLECTION: []} -class FakeServicePluginController(object): +class FakeServicePluginController(controller_utils.NeutronPecanController): resource = _SERVICE_PLUGIN_RESOURCE collection = _SERVICE_PLUGIN_COLLECTION @@ -58,7 +59,9 @@ class TestRootController(test_functional.PecanFunctionalTest): def setup_service_plugin(self): manager.NeutronManager.set_controller_for_resource( - _SERVICE_PLUGIN_COLLECTION, FakeServicePluginController()) + _SERVICE_PLUGIN_COLLECTION, + FakeServicePluginController(_SERVICE_PLUGIN_COLLECTION, + _SERVICE_PLUGIN_RESOURCE)) def _test_method_returns_code(self, method, code=200): api_method = getattr(self.app, method)