Merge "Make it possible to set None to REST API filters"

This commit is contained in:
Zuul 2019-11-19 09:12:47 +00:00 committed by Gerrit Code Review
commit a9b19b5f25
5 changed files with 102 additions and 4 deletions

View File

@ -334,14 +334,15 @@ class ExecutionsController(rest.RestController):
wtypes.text, types.uuid, wtypes.text, types.jsontype,
types.uuid, types.uuid, STATE_TYPES, wtypes.text,
types.jsontype, types.jsontype, wtypes.text,
wtypes.text, bool, types.uuid, bool)
wtypes.text, bool, types.uuid, bool, types.list)
def get_all(self, marker=None, limit=None, sort_keys='created_at',
sort_dirs='asc', fields='', workflow_name=None,
workflow_id=None, description=None, params=None,
task_execution_id=None, root_execution_id=None, state=None,
state_info=None, input=None, output=None, created_at=None,
updated_at=None, include_output=None, project_id=None,
all_projects=False):
all_projects=False, nulls=''):
"""Return all Executions.
:param marker: Optional. Pagination marker for large data sets.
@ -384,13 +385,18 @@ class ExecutionsController(rest.RestController):
Admin required.
:param all_projects: Optional. Get resources of all projects. Admin
required.
:param nulls: Optional. The names of the columns with null value in
the query.
"""
acl.enforce('executions:list', context.ctx())
db_models.WorkflowExecution.check_allowed_none_values(nulls)
if all_projects or project_id:
acl.enforce('executions:list:all_projects', context.ctx())
filters = filter_utils.create_filters_from_request_params(
none_values=nulls,
created_at=created_at,
workflow_name=workflow_name,
workflow_id=workflow_id,

View File

@ -123,6 +123,28 @@ class _MistralModelBase(oslo_models.ModelBase, oslo_models.TimestampMixin):
def __repr__(self):
return '%s %s' % (type(self).__name__, self.to_dict().__repr__())
@classmethod
def _get_nullable_column_names(cls):
return [c.name for c in cls.__table__.columns if c.nullable]
@classmethod
def check_allowed_none_values(cls, column_names):
"""Checks if the given columns can be assigned with None value.
:param column_names: The names of the columns to check.
"""
all_columns = cls.__table__.columns.keys()
nullable_columns = cls._get_nullable_column_names()
for col in column_names:
if col not in all_columns:
raise ValueError("'{}' is not a valid field name.".format(col))
if col not in nullable_columns:
raise ValueError(
"The field '{}' can't hold None value.".format(col)
)
MistralModelBase = declarative.declarative_base(cls=_MistralModelBase)

View File

@ -968,3 +968,26 @@ class TestExecutionsController(base.APITest):
self.assertTrue(
mock_get_execs.call_args[1].get('project_id', fake_project_id)
)
def test_get_all_with_nulls_not_valid(self):
resp = self.app.get(
'/v2/executions?limit=10&sort_keys=id&sort_dirs=asc&nulls=invalid',
expect_errors=True
)
self.assertEqual(500, resp.status_int)
self.assertIn(
"'invalid' is not a valid field name.",
resp.body.decode()
)
resp = self.app.get(
'/v2/executions?limit=10&sort_keys=id&sort_dirs=asc&nulls=id',
expect_errors=True
)
self.assertEqual(500, resp.status_int)
self.assertIn(
"The field 'id' can't hold None value.",
resp.body.decode()
)

View File

@ -0,0 +1,45 @@
# Copyright 2018 - Nokia, Inc.
#
# 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 mistral.tests.unit import base
from mistral.utils import filter_utils
class FilterUtilsTest(base.BaseTest):
def test_create_filters_with_nones(self):
expected_filters = {
'key2': {'eq': 'value2'},
'key1': {'eq': None}
}
filters = filter_utils.create_filters_from_request_params(
none_values=['key1'],
key1=None,
key2='value2',
key3=None,
)
self.assertEqual(expected_filters, filters)
del expected_filters['key1']
filters = filter_utils.create_filters_from_request_params(
none_values=[],
key1=None,
key2='value2',
key3=None,
)
self.assertEqual(expected_filters, filters)

View File

@ -15,16 +15,18 @@
import six
def create_filters_from_request_params(**params):
def create_filters_from_request_params(none_values=None, **params):
"""Create filters from REST request parameters.
:param none_values: field names, where the value is required to be None.
:param req_params: REST request parameters.
:return: filters dictionary.
"""
none_values = none_values or []
filters = {}
for column, data in params.items():
if data is not None:
if (data is None and column in none_values) or data is not None:
if isinstance(data, six.string_types):
f_type, value = _extract_filter_type_and_value(data)