Filtering support for actions
TODO: Add more tests. Change-Id: I110c2073b82c4ffb3a1f02e152937451395a1e87 Implements: blueprint mistral-items-filtering
This commit is contained in:
parent
4bdda3b6ed
commit
eebe77b20f
@ -29,6 +29,7 @@ from mistral import context
|
||||
from mistral.db.v2 import api as db_api
|
||||
from mistral import exceptions as exc
|
||||
from mistral.services import actions
|
||||
from mistral.utils import filter_utils
|
||||
from mistral.utils import rest_utils
|
||||
from mistral.workbook import parser as spec_parser
|
||||
|
||||
@ -138,11 +139,11 @@ class ActionsController(rest.RestController, hooks.HookController):
|
||||
@wsme_pecan.wsexpose(resources.Actions, types.uuid, int, types.uniquelist,
|
||||
types.list, types.uniquelist, wtypes.text,
|
||||
wtypes.text, resources.SCOPE_TYPES, wtypes.text,
|
||||
types.uniquelist, wtypes.text, wtypes.text,
|
||||
wtypes.text, bool, wtypes.text)
|
||||
wtypes.text, wtypes.text, wtypes.text, wtypes.text,
|
||||
wtypes.text)
|
||||
def get_all(self, marker=None, limit=None, sort_keys='name',
|
||||
sort_dirs='asc', fields='', created_at=None, name=None,
|
||||
scope=None, tag=None, tags=None, updated_at=None,
|
||||
scope=None, tags=None, updated_at=None,
|
||||
description=None, definition=None, is_system=None, input=None):
|
||||
"""Return all actions.
|
||||
|
||||
@ -168,9 +169,6 @@ class ActionsController(rest.RestController, hooks.HookController):
|
||||
:param input: Optional. Keep only resources with a specific input.
|
||||
:param description: Optional. Keep only resources with a specific
|
||||
description.
|
||||
:param tag: Optional. Keep only resources with a specific tag. If it is
|
||||
used with 'tags', it will be appended to the list of
|
||||
matching tags.
|
||||
:param tags: Optional. Keep only resources containing specific tags.
|
||||
:param created_at: Optional. Keep only resources created at a specific
|
||||
time and date.
|
||||
@ -182,13 +180,7 @@ class ActionsController(rest.RestController, hooks.HookController):
|
||||
"""
|
||||
acl.enforce('actions:list', context.ctx())
|
||||
|
||||
if tag is not None:
|
||||
if tags is None:
|
||||
tags = [tag]
|
||||
else:
|
||||
tags.append(tag)
|
||||
|
||||
filters = rest_utils.filters_to_dict(
|
||||
filters = filter_utils.create_filters_from_request_params(
|
||||
created_at=created_at,
|
||||
name=name,
|
||||
scope=scope,
|
||||
|
@ -27,6 +27,7 @@ from mistral import context
|
||||
from mistral.db.v2 import api as db_api
|
||||
from mistral.engine.rpc_backend import rpc
|
||||
from mistral import exceptions as exc
|
||||
from mistral.utils import filter_utils
|
||||
from mistral.utils import rest_utils
|
||||
from mistral.workflow import states
|
||||
from mistral.workflow import utils as wf_utils
|
||||
@ -178,13 +179,12 @@ class ActionExecutionsController(rest.RestController):
|
||||
@wsme_pecan.wsexpose(resources.ActionExecutions, types.uuid, int,
|
||||
types.uniquelist, types.list, types.uniquelist,
|
||||
wtypes.text, wtypes.text, wtypes.text,
|
||||
types.uniquelist, wtypes.text, wtypes.text,
|
||||
wtypes.text, types.uuid, wtypes.text, wtypes.text,
|
||||
bool, types.jsontype, types.jsontype, types.jsontype,
|
||||
wtypes.text)
|
||||
wtypes.text, wtypes.text, wtypes.text, types.uuid,
|
||||
wtypes.text, wtypes.text, bool, types.jsontype,
|
||||
types.jsontype, types.jsontype, wtypes.text)
|
||||
def get_all(self, marker=None, limit=None, sort_keys='created_at',
|
||||
sort_dirs='asc', fields='', created_at=None, name=None,
|
||||
tag=None, tags=None, updated_at=None, workflow_name=None,
|
||||
tags=None, updated_at=None, workflow_name=None,
|
||||
task_name=None, task_execution_id=None, state=None,
|
||||
state_info=None, accepted=None, input=None, output=None,
|
||||
params=None, description=None):
|
||||
@ -224,9 +224,6 @@ class ActionExecutionsController(rest.RestController):
|
||||
:param params: Optional. Keep only resources with specific parameters.
|
||||
:param description: Optional. Keep only resources with a specific
|
||||
description.
|
||||
:param tag: Optional. Keep only resources with a specific tag. If it is
|
||||
used with 'tags', it will be appended to the list of
|
||||
matching tags.
|
||||
:param tags: Optional. Keep only resources containing specific tags.
|
||||
:param created_at: Optional. Keep only resources created at a specific
|
||||
time and date.
|
||||
@ -235,13 +232,7 @@ class ActionExecutionsController(rest.RestController):
|
||||
"""
|
||||
acl.enforce('action_executions:list', context.ctx())
|
||||
|
||||
if tag is not None:
|
||||
if tags is None:
|
||||
tags = [tag]
|
||||
else:
|
||||
tags.append(tag)
|
||||
|
||||
filters = rest_utils.filters_to_dict(
|
||||
filters = filter_utils.create_filters_from_request_params(
|
||||
created_at=created_at,
|
||||
name=name,
|
||||
tags=tags,
|
||||
@ -299,13 +290,13 @@ class ActionExecutionsController(rest.RestController):
|
||||
class TasksActionExecutionController(rest.RestController):
|
||||
@wsme_pecan.wsexpose(resources.ActionExecutions, types.uuid, types.uuid,
|
||||
int, types.uniquelist, types.list, types.uniquelist,
|
||||
wtypes.text, wtypes.text, types.uniquelist,
|
||||
wtypes.text, types.uniquelist, wtypes.text,
|
||||
wtypes.text, wtypes.text, wtypes.text, wtypes.text,
|
||||
wtypes.text, wtypes.text, bool, types.jsontype,
|
||||
types.jsontype, types.jsontype, wtypes.text)
|
||||
wtypes.text, bool, types.jsontype, types.jsontype,
|
||||
types.jsontype, wtypes.text)
|
||||
def get_all(self, task_execution_id, marker=None, limit=None,
|
||||
sort_keys='created_at', sort_dirs='asc', fields='',
|
||||
created_at=None, name=None, tag=None, tags=None,
|
||||
created_at=None, name=None, tags=None,
|
||||
updated_at=None, workflow_name=None, task_name=None,
|
||||
state=None, state_info=None, accepted=None, input=None,
|
||||
output=None, params=None, description=None):
|
||||
@ -345,9 +336,6 @@ class TasksActionExecutionController(rest.RestController):
|
||||
:param params: Optional. Keep only resources with specific parameters.
|
||||
:param description: Optional. Keep only resources with a specific
|
||||
description.
|
||||
:param tag: Optional. Keep only resources with a specific tag. If it is
|
||||
used with 'tags', it will be appended to the list of
|
||||
matching tags.
|
||||
:param tags: Optional. Keep only resources containing specific tags.
|
||||
:param created_at: Optional. Keep only resources created at a specific
|
||||
time and date.
|
||||
@ -356,13 +344,7 @@ class TasksActionExecutionController(rest.RestController):
|
||||
"""
|
||||
acl.enforce('action_executions:list', context.ctx())
|
||||
|
||||
if tag is not None:
|
||||
if tags is None:
|
||||
tags = [tag]
|
||||
else:
|
||||
tags.append(tag)
|
||||
|
||||
filters = rest_utils.filters_to_dict(
|
||||
filters = filter_utils.create_filters_from_request_params(
|
||||
created_at=created_at,
|
||||
name=name,
|
||||
tags=tags,
|
||||
|
@ -23,6 +23,7 @@ from mistral.api.controllers.v2 import types
|
||||
from mistral import context
|
||||
from mistral.db.v2 import api as db_api
|
||||
from mistral.services import triggers
|
||||
from mistral.utils import filter_utils
|
||||
from mistral.utils import rest_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -130,7 +131,7 @@ class CronTriggersController(rest.RestController):
|
||||
"""
|
||||
acl.enforce('cron_triggers:list', context.ctx())
|
||||
|
||||
filters = rest_utils.filters_to_dict(
|
||||
filters = filter_utils.create_filters_from_request_params(
|
||||
created_at=created_at,
|
||||
name=name,
|
||||
updated_at=updated_at,
|
||||
|
@ -25,6 +25,7 @@ from mistral.api.controllers.v2 import types
|
||||
from mistral import context
|
||||
from mistral.db.v2 import api as db_api
|
||||
from mistral import exceptions as exceptions
|
||||
from mistral.utils import filter_utils
|
||||
from mistral.utils import rest_utils
|
||||
|
||||
|
||||
@ -71,7 +72,7 @@ class EnvironmentController(rest.RestController):
|
||||
"""
|
||||
acl.enforce('environments:list', context.ctx())
|
||||
|
||||
filters = rest_utils.filters_to_dict(
|
||||
filters = filter_utils.create_filters_from_request_params(
|
||||
created_at=created_at,
|
||||
name=name,
|
||||
updated_at=updated_at,
|
||||
|
@ -29,6 +29,7 @@ from mistral.db.v2 import api as db_api
|
||||
from mistral.engine.rpc_backend import rpc
|
||||
from mistral import exceptions as exc
|
||||
from mistral.services import workflows as wf_service
|
||||
from mistral.utils import filter_utils
|
||||
from mistral.utils import rest_utils
|
||||
from mistral.workflow import states
|
||||
|
||||
@ -260,7 +261,7 @@ class ExecutionsController(rest.RestController):
|
||||
"""
|
||||
acl.enforce('executions:list', context.ctx())
|
||||
|
||||
filters = rest_utils.filters_to_dict(
|
||||
filters = filter_utils.create_filters_from_request_params(
|
||||
created_at=created_at,
|
||||
workflow_name=workflow_name,
|
||||
workflow_id=workflow_id,
|
||||
|
@ -28,6 +28,7 @@ from mistral import context
|
||||
from mistral.db.v2 import api as db_api
|
||||
from mistral.engine.rpc_backend import rpc
|
||||
from mistral import exceptions as exc
|
||||
from mistral.utils import filter_utils
|
||||
from mistral.utils import rest_utils
|
||||
from mistral.workbook import parser as spec_parser
|
||||
from mistral.workflow import data_flow
|
||||
@ -93,7 +94,7 @@ class TaskExecutionsController(rest.RestController):
|
||||
"""
|
||||
acl.enforce('executions:list', context.ctx())
|
||||
|
||||
filters = rest_utils.filters_to_dict(
|
||||
filters = filter_utils.create_filters_from_request_params(
|
||||
task_execution_id=task_execution_id,
|
||||
created_at=created_at,
|
||||
workflow_name=workflow_name,
|
||||
@ -196,7 +197,7 @@ class TasksController(rest.RestController):
|
||||
"""
|
||||
acl.enforce('tasks:list', context.ctx())
|
||||
|
||||
filters = rest_utils.filters_to_dict(
|
||||
filters = filter_utils.create_filters_from_request_params(
|
||||
created_at=created_at,
|
||||
workflow_name=workflow_name,
|
||||
workflow_id=workflow_id,
|
||||
@ -339,7 +340,7 @@ class ExecutionTasksController(rest.RestController):
|
||||
"""
|
||||
acl.enforce('tasks:list', context.ctx())
|
||||
|
||||
filters = rest_utils.filters_to_dict(
|
||||
filters = filter_utils.create_filters_from_request_params(
|
||||
workflow_execution_id=workflow_execution_id,
|
||||
created_at=created_at,
|
||||
workflow_name=workflow_name,
|
||||
|
@ -28,6 +28,7 @@ from mistral.api.hooks import content_type as ct_hook
|
||||
from mistral import context
|
||||
from mistral.db.v2 import api as db_api
|
||||
from mistral.services import workbooks
|
||||
from mistral.utils import filter_utils
|
||||
from mistral.utils import rest_utils
|
||||
from mistral.workbook import parser as spec_parser
|
||||
|
||||
@ -95,11 +96,10 @@ class WorkbooksController(rest.RestController, hooks.HookController):
|
||||
@wsme_pecan.wsexpose(resources.Workbooks, types.uuid, int,
|
||||
types.uniquelist, types.list, types.uniquelist,
|
||||
wtypes.text, wtypes.text, wtypes.text,
|
||||
resources.SCOPE_TYPES, wtypes.text, types.uniquelist,
|
||||
wtypes.text)
|
||||
resources.SCOPE_TYPES, wtypes.text, wtypes.text)
|
||||
def get_all(self, marker=None, limit=None, sort_keys='created_at',
|
||||
sort_dirs='asc', fields='', created_at=None,
|
||||
definition=None, name=None, scope=None, tag=None, tags=None,
|
||||
definition=None, name=None, scope=None, tags=None,
|
||||
updated_at=None):
|
||||
"""Return a list of workbooks.
|
||||
|
||||
@ -119,9 +119,6 @@ class WorkbooksController(rest.RestController, hooks.HookController):
|
||||
:param name: Optional. Keep only resources with a specific name.
|
||||
:param definition: Optional. Keep only resources with a specific
|
||||
definition.
|
||||
:param tag: Optional. Keep only resources with a specific tag. If it is
|
||||
used with 'tags', it will be appended to the list of
|
||||
matching tags.
|
||||
:param tags: Optional. Keep only resources containing specific tags.
|
||||
:param scope: Optional. Keep only resources with a specific scope.
|
||||
:param created_at: Optional. Keep only resources created at a specific
|
||||
@ -134,13 +131,7 @@ class WorkbooksController(rest.RestController, hooks.HookController):
|
||||
"""
|
||||
acl.enforce('workbooks:list', context.ctx())
|
||||
|
||||
if tag is not None:
|
||||
if tags is None:
|
||||
tags = [tag]
|
||||
else:
|
||||
tags.append(tag)
|
||||
|
||||
filters = rest_utils.filters_to_dict(
|
||||
filters = filter_utils.create_filters_from_request_params(
|
||||
created_at=created_at,
|
||||
definition=definition,
|
||||
name=name,
|
||||
|
@ -32,6 +32,7 @@ from mistral import context
|
||||
from mistral.db.v2 import api as db_api
|
||||
from mistral import exceptions as exc
|
||||
from mistral.services import workflows
|
||||
from mistral.utils import filter_utils
|
||||
from mistral.utils import rest_utils
|
||||
from mistral.workbook import parser as spec_parser
|
||||
|
||||
@ -169,11 +170,11 @@ class WorkflowsController(rest.RestController, hooks.HookController):
|
||||
@wsme_pecan.wsexpose(resources.Workflows, types.uuid, int,
|
||||
types.uniquelist, types.list, types.uniquelist,
|
||||
wtypes.text, wtypes.text, wtypes.text, wtypes.text,
|
||||
types.uniquelist, resources.SCOPE_TYPES, types.uuid,
|
||||
wtypes.text, wtypes.text)
|
||||
resources.SCOPE_TYPES, types.uuid, wtypes.text,
|
||||
wtypes.text)
|
||||
def get_all(self, marker=None, limit=None, sort_keys='created_at',
|
||||
sort_dirs='asc', fields='', name=None, input=None,
|
||||
definition=None, tag=None, tags=None, scope=None,
|
||||
definition=None, tags=None, scope=None,
|
||||
project_id=None, created_at=None, updated_at=None):
|
||||
"""Return a list of workflows.
|
||||
|
||||
@ -194,9 +195,6 @@ class WorkflowsController(rest.RestController, hooks.HookController):
|
||||
:param input: Optional. Keep only resources with a specific input.
|
||||
:param definition: Optional. Keep only resources with a specific
|
||||
definition.
|
||||
:param tag: Optional. Keep only resources with a specific tag. If it is
|
||||
used with 'tags', it will be appended to the list of
|
||||
matching tags.
|
||||
:param tags: Optional. Keep only resources containing specific tags.
|
||||
:param scope: Optional. Keep only resources with a specific scope.
|
||||
:param project_id: Optional. The same as the requester project_id
|
||||
@ -208,13 +206,7 @@ class WorkflowsController(rest.RestController, hooks.HookController):
|
||||
"""
|
||||
acl.enforce('workflows:list', context.ctx())
|
||||
|
||||
if tag is not None:
|
||||
if tags is None:
|
||||
tags = [tag]
|
||||
else:
|
||||
tags.append(tag)
|
||||
|
||||
filters = rest_utils.filters_to_dict(
|
||||
filters = filter_utils.create_filters_from_request_params(
|
||||
created_at=created_at,
|
||||
name=name,
|
||||
scope=scope,
|
||||
|
@ -30,6 +30,7 @@ from sqlalchemy.sql.expression import Insert
|
||||
from mistral.db.sqlalchemy import base as b
|
||||
from mistral.db.sqlalchemy import model_base as mb
|
||||
from mistral.db.sqlalchemy import sqlite_lock
|
||||
from mistral.db.v2.sqlalchemy import filters as db_filters
|
||||
from mistral.db.v2.sqlalchemy import models
|
||||
from mistral import exceptions as exc
|
||||
from mistral.services import security
|
||||
@ -173,7 +174,7 @@ def _delete_all(model, session=None, **kwargs):
|
||||
|
||||
|
||||
def _get_collection(model, insecure=False, limit=None, marker=None,
|
||||
sort_keys=None, sort_dirs=None, fields=None, **kwargs):
|
||||
sort_keys=None, sort_dirs=None, fields=None, **filters):
|
||||
columns = (
|
||||
tuple([getattr(model, f) for f in fields if hasattr(model, f)])
|
||||
if fields else ()
|
||||
@ -181,21 +182,7 @@ def _get_collection(model, insecure=False, limit=None, marker=None,
|
||||
|
||||
query = (b.model_query(model, *columns) if insecure
|
||||
else _secure_query(model, *columns))
|
||||
query = query.filter_by(**kwargs)
|
||||
|
||||
tags = kwargs.pop('tags', None)
|
||||
|
||||
# To match the tag list, a resource must contain at least all of the
|
||||
# tags present in the filter parameter.
|
||||
if tags:
|
||||
tag_attr = getattr(model, 'tags')
|
||||
|
||||
if len(tags) == 1:
|
||||
expr = tag_attr.contains(tags)
|
||||
else:
|
||||
expr = sa.and_(*[tag_attr.contains(tag) for tag in tags])
|
||||
|
||||
query = query.filter(expr)
|
||||
query = db_filters.apply_filters(query, model, **filters)
|
||||
|
||||
query = _paginate_query(
|
||||
model,
|
||||
|
63
mistral/db/v2/sqlalchemy/filters.py
Normal file
63
mistral/db/v2/sqlalchemy/filters.py
Normal file
@ -0,0 +1,63 @@
|
||||
# Copyright 2016 NEC Corporation. All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import six
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def apply_filters(query, model, **filters):
|
||||
filter_dict = {}
|
||||
|
||||
for key, value in six.iteritems(filters):
|
||||
column_attr = getattr(model, key)
|
||||
if isinstance(value, dict):
|
||||
if 'in' in value:
|
||||
query = query.filter(column_attr.in_(value['in']))
|
||||
elif 'nin' in value:
|
||||
query = query.filter(~column_attr.in_(value['nin']))
|
||||
elif 'neq' in value:
|
||||
query = query.filter(column_attr != value['neq'])
|
||||
elif 'gt' in value:
|
||||
query = query.filter(column_attr > value['gt'])
|
||||
elif 'gte' in value:
|
||||
query = query.filter(column_attr >= value['gte'])
|
||||
elif 'lt' in value:
|
||||
query = query.filter(column_attr < value['lt'])
|
||||
elif 'lte' in value:
|
||||
query = query.filter(column_attr <= value['lte'])
|
||||
elif 'eq' in value:
|
||||
query = query.filter(column_attr == value['eq'])
|
||||
else:
|
||||
filter_dict[key] = value
|
||||
|
||||
# We need to handle tag case seprately. As tag datatype is MutableList.
|
||||
# TODO(hparekh): Need to think how can we get rid of this.
|
||||
tags = filters.pop('tags', None)
|
||||
|
||||
# To match the tag list, a resource must contain at least all of the
|
||||
# tags present in the filter parameter.
|
||||
if tags:
|
||||
tag_attr = getattr(model, 'tags')
|
||||
|
||||
if not isinstance(tags, list):
|
||||
expr = tag_attr.contains(tags)
|
||||
else:
|
||||
expr = sa.and_(*[tag_attr.contains(tag) for tag in tags])
|
||||
|
||||
query = query.filter(expr)
|
||||
|
||||
if filter_dict:
|
||||
query = query.filter_by(**filter_dict)
|
||||
|
||||
return query
|
@ -26,6 +26,7 @@ from mistral.db.v2.sqlalchemy import models as db_models
|
||||
from mistral import exceptions as exc
|
||||
from mistral.services import security
|
||||
from mistral.tests.unit import base as test_base
|
||||
from mistral.utils import filter_utils
|
||||
|
||||
|
||||
user_context = test_base.get_context(default=False)
|
||||
@ -567,6 +568,15 @@ ACTION_DEFINITIONS = [
|
||||
'attributes': None,
|
||||
'project_id': '<default-project>'
|
||||
},
|
||||
{
|
||||
'name': 'action3',
|
||||
'description': 'Action #3',
|
||||
'is_system': False,
|
||||
'tags': ['mc', 'abc'],
|
||||
'action_class': 'mypackage.my_module.Action3',
|
||||
'attributes': None,
|
||||
'project_id': '<default-project>'
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@ -605,6 +615,157 @@ class ActionDefinitionTest(SQLAlchemyTest):
|
||||
ACTION_DEFINITIONS[0]
|
||||
)
|
||||
|
||||
def test_filter_action_definitions_by_equal_value(self):
|
||||
db_api.create_action_definition(ACTION_DEFINITIONS[0])
|
||||
db_api.create_action_definition(ACTION_DEFINITIONS[1])
|
||||
|
||||
created2 = db_api.create_action_definition(ACTION_DEFINITIONS[2])
|
||||
_filter = filter_utils.create_or_update_filter(
|
||||
'is_system',
|
||||
False,
|
||||
'eq'
|
||||
)
|
||||
fetched = db_api.get_action_definitions(**_filter)
|
||||
|
||||
self.assertEqual(1, len(fetched))
|
||||
self.assertEqual(created2, fetched[0])
|
||||
|
||||
def test_filter_action_definitions_by_notEqual_value(self):
|
||||
created0 = db_api.create_action_definition(ACTION_DEFINITIONS[0])
|
||||
created1 = db_api.create_action_definition(ACTION_DEFINITIONS[1])
|
||||
|
||||
db_api.create_action_definition(ACTION_DEFINITIONS[2])
|
||||
|
||||
_filter = filter_utils.create_or_update_filter(
|
||||
'is_system',
|
||||
False,
|
||||
'neq'
|
||||
)
|
||||
fetched = db_api.get_action_definitions(**_filter)
|
||||
|
||||
self.assertEqual(2, len(fetched))
|
||||
self.assertEqual(created0, fetched[0])
|
||||
self.assertEqual(created1, fetched[1])
|
||||
|
||||
def test_filter_action_definitions_by_greaterThan_value(self):
|
||||
created0 = db_api.create_action_definition(ACTION_DEFINITIONS[0])
|
||||
created1 = db_api.create_action_definition(ACTION_DEFINITIONS[1])
|
||||
created2 = db_api.create_action_definition(ACTION_DEFINITIONS[2])
|
||||
|
||||
_filter = filter_utils.create_or_update_filter(
|
||||
'created_at',
|
||||
created0['created_at'],
|
||||
'gt'
|
||||
)
|
||||
fetched = db_api.get_action_definitions(**_filter)
|
||||
|
||||
self.assertEqual(2, len(fetched))
|
||||
self.assertEqual(created1, fetched[0])
|
||||
self.assertEqual(created2, fetched[1])
|
||||
|
||||
def test_filter_action_definitions_by_greaterThanEqual_value(self):
|
||||
created0 = db_api.create_action_definition(ACTION_DEFINITIONS[0])
|
||||
created1 = db_api.create_action_definition(ACTION_DEFINITIONS[1])
|
||||
created2 = db_api.create_action_definition(ACTION_DEFINITIONS[2])
|
||||
|
||||
_filter = filter_utils.create_or_update_filter(
|
||||
'created_at',
|
||||
created0['created_at'],
|
||||
'gte'
|
||||
)
|
||||
fetched = db_api.get_action_definitions(**_filter)
|
||||
|
||||
self.assertEqual(3, len(fetched))
|
||||
self.assertEqual(created0, fetched[0])
|
||||
self.assertEqual(created1, fetched[1])
|
||||
self.assertEqual(created2, fetched[2])
|
||||
|
||||
def test_filter_action_definitions_by_lessThan_value(self):
|
||||
created0 = db_api.create_action_definition(ACTION_DEFINITIONS[0])
|
||||
created1 = db_api.create_action_definition(ACTION_DEFINITIONS[1])
|
||||
created2 = db_api.create_action_definition(ACTION_DEFINITIONS[2])
|
||||
|
||||
_filter = filter_utils.create_or_update_filter(
|
||||
'created_at',
|
||||
created2['created_at'],
|
||||
'lt'
|
||||
)
|
||||
fetched = db_api.get_action_definitions(**_filter)
|
||||
|
||||
self.assertEqual(2, len(fetched))
|
||||
self.assertEqual(created0, fetched[0])
|
||||
self.assertEqual(created1, fetched[1])
|
||||
|
||||
def test_filter_action_definitions_by_lessThanEqual_value(self):
|
||||
created0 = db_api.create_action_definition(ACTION_DEFINITIONS[0])
|
||||
created1 = db_api.create_action_definition(ACTION_DEFINITIONS[1])
|
||||
created2 = db_api.create_action_definition(ACTION_DEFINITIONS[2])
|
||||
|
||||
_filter = filter_utils.create_or_update_filter(
|
||||
'created_at',
|
||||
created2['created_at'],
|
||||
'lte'
|
||||
)
|
||||
fetched = db_api.get_action_definitions(**_filter)
|
||||
|
||||
self.assertEqual(3, len(fetched))
|
||||
self.assertEqual(created0, fetched[0])
|
||||
self.assertEqual(created1, fetched[1])
|
||||
self.assertEqual(created2, fetched[2])
|
||||
|
||||
def test_filter_action_definitions_by_values_in_list(self):
|
||||
created0 = db_api.create_action_definition(ACTION_DEFINITIONS[0])
|
||||
created1 = db_api.create_action_definition(ACTION_DEFINITIONS[1])
|
||||
|
||||
db_api.create_action_definition(ACTION_DEFINITIONS[2])
|
||||
|
||||
_filter = filter_utils.create_or_update_filter(
|
||||
'created_at',
|
||||
[created0['created_at'], created1['created_at']],
|
||||
'in'
|
||||
)
|
||||
fetched = db_api.get_action_definitions(**_filter)
|
||||
|
||||
self.assertEqual(2, len(fetched))
|
||||
self.assertEqual(created0, fetched[0])
|
||||
self.assertEqual(created1, fetched[1])
|
||||
|
||||
def test_filter_action_definitions_by_values_notin_list(self):
|
||||
created0 = db_api.create_action_definition(ACTION_DEFINITIONS[0])
|
||||
created1 = db_api.create_action_definition(ACTION_DEFINITIONS[1])
|
||||
created2 = db_api.create_action_definition(ACTION_DEFINITIONS[2])
|
||||
|
||||
_filter = filter_utils.create_or_update_filter(
|
||||
'created_at',
|
||||
[created0['created_at'], created1['created_at']],
|
||||
'nin'
|
||||
)
|
||||
fetched = db_api.get_action_definitions(**_filter)
|
||||
|
||||
self.assertEqual(1, len(fetched))
|
||||
self.assertEqual(created2, fetched[0])
|
||||
|
||||
def test_filter_action_definitions_by_multiple_columns(self):
|
||||
created0 = db_api.create_action_definition(ACTION_DEFINITIONS[0])
|
||||
created1 = db_api.create_action_definition(ACTION_DEFINITIONS[1])
|
||||
|
||||
db_api.create_action_definition(ACTION_DEFINITIONS[2])
|
||||
|
||||
_filter = filter_utils.create_or_update_filter(
|
||||
'created_at',
|
||||
[created0['created_at'], created1['created_at']],
|
||||
'in'
|
||||
)
|
||||
_filter = filter_utils.create_or_update_filter(
|
||||
'is_system',
|
||||
True,
|
||||
'neq',
|
||||
_filter
|
||||
)
|
||||
fetched = db_api.get_action_definitions(**_filter)
|
||||
|
||||
self.assertEqual(0, len(fetched))
|
||||
|
||||
def test_update_action_definition_with_name(self):
|
||||
created = db_api.create_action_definition(ACTION_DEFINITIONS[0])
|
||||
|
||||
|
91
mistral/utils/filter_utils.py
Normal file
91
mistral/utils/filter_utils.py
Normal file
@ -0,0 +1,91 @@
|
||||
# Copyright 2016 NEC Corporation. All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import six
|
||||
|
||||
|
||||
def create_filters_from_request_params(**params):
|
||||
"""Create filters from REST request parameters.
|
||||
|
||||
:param req_params: REST request parameters.
|
||||
:return: filters dictionary.
|
||||
"""
|
||||
filters = {}
|
||||
for column, data in six.iteritems(params):
|
||||
if data is not None:
|
||||
if isinstance(data, six.string_types):
|
||||
f_type, value = _extract_filter_type_and_value(data)
|
||||
create_or_update_filter(column, value, f_type, filters)
|
||||
else:
|
||||
create_or_update_filter(column, data, _filter=filters)
|
||||
return filters
|
||||
|
||||
|
||||
def create_or_update_filter(column, value, filter_type='eq', _filter=None):
|
||||
"""Create or Update filter.
|
||||
|
||||
:param column: Column name by which user want to filter.
|
||||
:param value: Column value.
|
||||
:param filter_type: filter type. Filter type can be
|
||||
'eq', 'neq', 'gt', 'gte', 'lte', 'in',
|
||||
'lt', 'nin'. Default is 'eq'.
|
||||
:parma _filter: Optional. If provided same filter dictionary will
|
||||
be updated.
|
||||
:return: filter dictionary.
|
||||
|
||||
"""
|
||||
if _filter is None:
|
||||
_filter = {}
|
||||
_filter[column] = {filter_type: value}
|
||||
|
||||
return _filter
|
||||
|
||||
|
||||
def _extract_filter_type_and_value(data):
|
||||
"""Extract filter type and its value from the data.
|
||||
|
||||
:param data: REST parameter value from which filter type and
|
||||
value can be get. It should be in format of
|
||||
'filter_type:value'.
|
||||
:return: filter type and value.
|
||||
"""
|
||||
if data.startswith("in:"):
|
||||
value = list(six.text_type(data[3:]).split(","))
|
||||
filter_type = 'in'
|
||||
elif data.startswith("nin:"):
|
||||
value = list(six.text_type(data[4:]).split(","))
|
||||
filter_type = 'nin'
|
||||
elif data.startswith("neq:"):
|
||||
value = six.text_type(data[4:])
|
||||
filter_type = 'neq'
|
||||
elif data.startswith("gt:"):
|
||||
value = six.text_type(data[3:])
|
||||
filter_type = 'gt'
|
||||
elif data.startswith("gte:"):
|
||||
value = six.text_type(data[4:])
|
||||
filter_type = 'gte'
|
||||
elif data.startswith("lt:"):
|
||||
value = six.text_type(data[3:])
|
||||
filter_type = 'lt'
|
||||
elif data.startswith("lte:"):
|
||||
value = six.text_type(data[4:])
|
||||
filter_type = 'lte'
|
||||
elif data.startswith("eq:"):
|
||||
value = six.text_type(data[3:])
|
||||
filter_type = 'eq'
|
||||
else:
|
||||
value = data
|
||||
filter_type = 'eq'
|
||||
|
||||
return filter_type, value
|
@ -12,6 +12,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
from oslo_log import log as logging
|
||||
from tempest.lib import exceptions
|
||||
from tempest import test
|
||||
@ -125,6 +126,162 @@ class ActionTestsV2(base.TestCase):
|
||||
context.resp_body.get('faultstring')
|
||||
)
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_get_list_actions_equalto_filter(self):
|
||||
resp, body = self.client.create_action('action_v2.yaml')
|
||||
self.assertEqual(201, resp.status)
|
||||
|
||||
resp, body = self.client.get_list_obj(
|
||||
'actions?is_system=False'
|
||||
)
|
||||
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertNotEqual([], body['actions'])
|
||||
|
||||
for act in body['actions']:
|
||||
self.assertFalse(act['is_system'])
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_get_list_actions_notEqualto_filter(self):
|
||||
resp, body = self.client.create_action('action_v2.yaml')
|
||||
self.assertEqual(201, resp.status)
|
||||
|
||||
resp, body = self.client.get_list_obj(
|
||||
'actions?is_system=neq:False'
|
||||
)
|
||||
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertNotEqual([], body['actions'])
|
||||
|
||||
for act in body['actions']:
|
||||
self.assertTrue(act['is_system'])
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_get_list_actions_inList_filter(self):
|
||||
resp, body = self.client.create_action('action_v2.yaml')
|
||||
self.assertEqual(201, resp.status)
|
||||
|
||||
created_acts = [action['name'] for action in body['actions']]
|
||||
_, body = self.client.get_object('actions', created_acts[0])
|
||||
time = body['created_at']
|
||||
resp, body = self.client.get_list_obj(
|
||||
'actions?created_at=in:' + time.replace(' ', '%20')
|
||||
)
|
||||
|
||||
self.assertEqual(200, resp.status)
|
||||
action_names = [action['name'] for action in body['actions']]
|
||||
self.assertListEqual(created_acts, action_names)
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_get_list_actions_notinList_filter(self):
|
||||
resp, body = self.client.create_action('action_v2.yaml')
|
||||
self.assertEqual(201, resp.status)
|
||||
|
||||
created_acts = [action['name'] for action in body['actions']]
|
||||
_, body = self.client.get_object('actions', created_acts[0])
|
||||
time = body['created_at']
|
||||
resp, body = self.client.get_list_obj(
|
||||
'actions?created_at=nin:' + time.replace(' ', '%20')
|
||||
)
|
||||
|
||||
self.assertEqual(200, resp.status)
|
||||
action_names = [action['name'] for action in body['actions']]
|
||||
for act in created_acts:
|
||||
self.assertNotIn(act, action_names)
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_get_list_actions_greaterThan_filter(self):
|
||||
time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
resp, body = self.client.get_list_obj(
|
||||
'actions?created_at=gt:' + time.replace(' ', '%20')
|
||||
)
|
||||
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual([], body['actions'])
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_get_list_actions_greaterThanEqualto_filter(self):
|
||||
resp, body = self.client.create_action('action_v2.yaml')
|
||||
self.assertEqual(201, resp.status)
|
||||
|
||||
created_acts = [action['name'] for action in body['actions']]
|
||||
_, body = self.client.get_object('actions', created_acts[0])
|
||||
time = body['created_at']
|
||||
resp, body = self.client.get_list_obj(
|
||||
'actions?created_at=gte:' + time.replace(' ', '%20')
|
||||
)
|
||||
|
||||
actions = [action['name'] for action in body['actions']]
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertIn(created_acts[0], actions)
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_get_list_actions_lessThan_filter(self):
|
||||
resp, body = self.client.create_action('action_v2.yaml')
|
||||
self.assertEqual(201, resp.status)
|
||||
|
||||
created_acts = [action['name'] for action in body['actions']]
|
||||
_, body = self.client.get_object('actions', created_acts[0])
|
||||
time = body['created_at']
|
||||
resp, body = self.client.get_list_obj(
|
||||
'actions?created_at=lt:' + time.replace(' ', '%20')
|
||||
)
|
||||
|
||||
actions = [action['name'] for action in body['actions']]
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertNotIn(created_acts[0], actions)
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_get_list_actions_lessThanEqualto_filter(self):
|
||||
resp, body = self.client.create_action('action_v2.yaml')
|
||||
self.assertEqual(201, resp.status)
|
||||
|
||||
created_acts = [action['name'] for action in body['actions']]
|
||||
_, body = self.client.get_object('actions', created_acts[0])
|
||||
time = body['created_at']
|
||||
resp, body = self.client.get_list_obj(
|
||||
'actions?created_at=lte:' + time.replace(' ', '%20')
|
||||
)
|
||||
|
||||
actions = [action['name'] for action in body['actions']]
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertIn(created_acts[0], actions)
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_get_list_actions_multiple_filter(self):
|
||||
resp, body = self.client.create_action('action_v2.yaml')
|
||||
self.assertEqual(201, resp.status)
|
||||
|
||||
created_acts = [action['name'] for action in body['actions']]
|
||||
_, body = self.client.get_object('actions', created_acts[0])
|
||||
time = body['created_at']
|
||||
resp, body = self.client.get_list_obj(
|
||||
'actions?created_at=lte:' + time.replace(' ', '%20') +
|
||||
'&is_system=False'
|
||||
)
|
||||
|
||||
actions = [action['name'] for action in body['actions']]
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertIn(created_acts[0], actions)
|
||||
|
||||
@test.attr(type='negative')
|
||||
def test_get_list_actions_invalid_filter(self):
|
||||
self.assertRaises(
|
||||
exceptions.BadRequest,
|
||||
self.client.get_list_obj,
|
||||
'actions?is_system<False'
|
||||
)
|
||||
self.assertRaises(
|
||||
exceptions.BadRequest,
|
||||
self.client.get_list_obj,
|
||||
'actions?is_system!=False'
|
||||
)
|
||||
self.assertRaises(
|
||||
exceptions.BadRequest,
|
||||
self.client.get_list_obj,
|
||||
'actions?created_at>2016-02-23%2008:51:26'
|
||||
)
|
||||
|
||||
@test.attr(type='sanity')
|
||||
def test_create_and_delete_few_actions(self):
|
||||
resp, body = self.client.create_action('action_v2.yaml')
|
||||
|
Loading…
Reference in New Issue
Block a user