Merge "Add "fields" filter support on single objects"
This commit is contained in:
commit
c9bd45d073
mistral
api/controllers
db
tests/unit/api/v2
test_action_executions.pytest_cron_triggers.pytest_environment.pytest_event_trigger.pytest_executions.pytest_tasks.pytest_workbooks.pytest_workflows.py
utils
@ -51,8 +51,13 @@ class Resource(wtypes.Base):
|
||||
return cls.from_tuples(d.items())
|
||||
|
||||
@classmethod
|
||||
def from_db_model(cls, db_model):
|
||||
return cls.from_tuples(db_model.iter_columns())
|
||||
def from_db_model(cls, db_model, fields=()):
|
||||
if isinstance(db_model, tuple):
|
||||
db_tuples = zip(fields, db_model)
|
||||
else:
|
||||
db_tuples = db_model.iter_columns(fields=fields)
|
||||
|
||||
return cls.from_tuples(db_tuples)
|
||||
|
||||
def __str__(self):
|
||||
"""WSME based implementation of __str__."""
|
||||
|
@ -79,6 +79,9 @@ class ActionsController(rest.RestController, hooks.HookController):
|
||||
|
||||
:param identifier: ID or name of the Action to get.
|
||||
:param namespace: The namespace of the action.
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's not provided.
|
||||
"""
|
||||
|
||||
acl.enforce('actions:get', context.ctx())
|
||||
@ -251,7 +254,7 @@ class ActionsController(rest.RestController, hooks.HookController):
|
||||
Default: asc.
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's provided, since it will be used when
|
||||
fields if it's not provided, since it will be used when
|
||||
constructing 'next' link.
|
||||
:param name: Optional. Keep only resources with a specific name.
|
||||
:param scope: Optional. Keep only resources with a specific scope.
|
||||
|
@ -52,25 +52,40 @@ def _load_deferred_output_field(action_ex):
|
||||
|
||||
# Use retries to prevent possible failures.
|
||||
@rest_utils.rest_retry_on_db_error
|
||||
def _get_action_execution(id):
|
||||
def _get_action_execution(id, fields=()):
|
||||
if fields and 'id' not in fields:
|
||||
fields.insert(0, 'id')
|
||||
|
||||
with db_api.transaction():
|
||||
return _get_action_execution_resource(db_api.get_action_execution(id))
|
||||
return _get_action_execution_resource(
|
||||
db_api.get_action_execution(id),
|
||||
fields=fields
|
||||
)
|
||||
|
||||
|
||||
def _get_action_execution_resource(action_ex):
|
||||
def _get_action_execution_resource(action_ex, fields=()):
|
||||
_load_deferred_output_field(action_ex)
|
||||
|
||||
return _get_action_execution_resource_for_list(action_ex)
|
||||
if fields and 'id' not in fields:
|
||||
fields.insert(0, 'id')
|
||||
|
||||
return _get_action_execution_resource_for_list(action_ex, fields=fields)
|
||||
|
||||
|
||||
def _get_action_execution_resource_for_list(action_ex):
|
||||
def _get_action_execution_resource_for_list(action_ex, fields=()):
|
||||
# TODO(nmakhotkin): Get rid of using dicts for constructing resources.
|
||||
# TODO(nmakhotkin): Use db_model for this instead.
|
||||
res = resources.ActionExecution.from_db_model(action_ex)
|
||||
|
||||
task_name = (action_ex.task_execution.name
|
||||
if action_ex.task_execution else None)
|
||||
setattr(res, 'task_name', task_name)
|
||||
# field_task_name_needed = 'task_name' in fields
|
||||
# if field_task_name_needed:
|
||||
# fields.remove('task_name')
|
||||
|
||||
res = resources.ActionExecution.from_db_model(action_ex, fields=fields)
|
||||
|
||||
# if not fields or field_task_name_needed:
|
||||
# task_name = (action_ex.task_execution.name
|
||||
# if action_ex.task_execution else None)
|
||||
# setattr(res, 'task_name', task_name)
|
||||
|
||||
return res
|
||||
|
||||
@ -95,7 +110,7 @@ def _get_action_executions(task_execution_id=None, marker=None, limit=None,
|
||||
or less than that of sort_keys.
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's provided, since it will be used when
|
||||
fields if it's not provided, since it will be used when
|
||||
constructing 'next' link.
|
||||
:param filters: Optional. A list of filters to apply to the result.
|
||||
"""
|
||||
@ -124,17 +139,21 @@ def _get_action_executions(task_execution_id=None, marker=None, limit=None,
|
||||
|
||||
class ActionExecutionsController(rest.RestController):
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(resources.ActionExecution, wtypes.text)
|
||||
def get(self, id):
|
||||
@wsme_pecan.wsexpose(resources.ActionExecution, wtypes.text,
|
||||
types.uniquelist)
|
||||
def get(self, id, fields=None):
|
||||
"""Return the specified action_execution.
|
||||
|
||||
:param id: UUID of action execution to retrieve
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's not provided.
|
||||
"""
|
||||
acl.enforce('action_executions:get', context.ctx())
|
||||
|
||||
LOG.debug("Fetch action_execution [id=%s]", id)
|
||||
|
||||
return _get_action_execution(id)
|
||||
return _get_action_execution(id, fields=fields)
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(resources.ActionExecution,
|
||||
@ -248,7 +267,7 @@ class ActionExecutionsController(rest.RestController):
|
||||
or less than that of sort_keys.
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's provided, since it will be used when
|
||||
fields if it's not provided, since it will be used when
|
||||
constructing 'next' link.
|
||||
:param name: Optional. Keep only resources with a specific name.
|
||||
:param workflow_name: Optional. Keep only resources with a specific
|
||||
@ -379,7 +398,7 @@ class TasksActionExecutionController(rest.RestController):
|
||||
or less than that of sort_keys.
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's provided, since it will be used when
|
||||
fields if it's not provided, since it will be used when
|
||||
constructing 'next' link.
|
||||
:param name: Optional. Keep only resources with a specific name.
|
||||
:param workflow_name: Optional. Keep only resources with a specific
|
||||
@ -444,15 +463,19 @@ class TasksActionExecutionController(rest.RestController):
|
||||
)
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(resources.ActionExecution, wtypes.text, wtypes.text)
|
||||
def get(self, task_execution_id, action_ex_id):
|
||||
@wsme_pecan.wsexpose(resources.ActionExecution, wtypes.text, wtypes.text,
|
||||
types.uniquelist)
|
||||
def get(self, task_execution_id, action_ex_id, fields=()):
|
||||
"""Return the specified action_execution.
|
||||
|
||||
:param task_execution_id: Task execution UUID
|
||||
:param action_ex_id: Action execution UUID
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's not provided.
|
||||
"""
|
||||
acl.enforce('action_executions:get', context.ctx())
|
||||
|
||||
LOG.debug("Fetch action_execution [id=%s]", action_ex_id)
|
||||
|
||||
return _get_action_execution(action_ex_id)
|
||||
return _get_action_execution(action_ex_id, fields=fields)
|
||||
|
@ -187,8 +187,9 @@ class CodeSourcesController(rest.RestController, hooks.HookController):
|
||||
)
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(resources.CodeSource, wtypes.text, wtypes.text)
|
||||
def get(self, identifier, namespace=''):
|
||||
@wsme_pecan.wsexpose(resources.CodeSource, wtypes.text,
|
||||
wtypes.text, types.uniquelist)
|
||||
def get(self, identifier, namespace='', fields=''):
|
||||
"""Return a code source.
|
||||
|
||||
:param identifier: Name or UUID of the code source to retrieve.
|
||||
@ -202,13 +203,17 @@ class CodeSourcesController(rest.RestController, hooks.HookController):
|
||||
identifier,
|
||||
namespace
|
||||
)
|
||||
if fields and 'id' not in fields:
|
||||
fields.insert(0, 'id')
|
||||
|
||||
db_model = rest_utils.rest_retry_on_db_error(
|
||||
db_api.get_code_source)(
|
||||
identifier=identifier,
|
||||
namespace=namespace
|
||||
namespace=namespace,
|
||||
fields=fields
|
||||
)
|
||||
|
||||
if fields:
|
||||
return resources.CodeSource.from_tuples(zip(fields, db_model))
|
||||
return resources.CodeSource.from_db_model(db_model)
|
||||
|
||||
@rest_utils.wrap_pecan_controller_exception
|
||||
|
@ -31,22 +31,30 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
class CronTriggersController(rest.RestController):
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(resources.CronTrigger, wtypes.text)
|
||||
def get(self, identifier):
|
||||
@wsme_pecan.wsexpose(resources.CronTrigger,
|
||||
wtypes.text, types.uniquelist)
|
||||
def get(self, identifier, fields=''):
|
||||
"""Returns the named cron_trigger.
|
||||
|
||||
:param identifier: Id or name of cron trigger to retrieve
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's not provided.
|
||||
"""
|
||||
acl.enforce('cron_triggers:get', context.ctx())
|
||||
|
||||
LOG.debug('Fetch cron trigger [identifier=%s]', identifier)
|
||||
|
||||
if fields and 'id' not in fields:
|
||||
fields.insert(0, 'id')
|
||||
|
||||
# Use retries to prevent possible failures.
|
||||
db_model = rest_utils.rest_retry_on_db_error(
|
||||
db_api.get_cron_trigger
|
||||
)(identifier)
|
||||
|
||||
return resources.CronTrigger.from_db_model(db_model)
|
||||
)(identifier, fields=fields)
|
||||
if fields:
|
||||
return resources.CronTrigger.from_tuples(zip(fields, db_model))
|
||||
return resources.CronTrigger.from_db_model(db_model, fields=fields)
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(
|
||||
@ -124,7 +132,7 @@ class CronTriggersController(rest.RestController):
|
||||
or less than that of sort_keys.
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's provided, since it will be used when
|
||||
fields if it's not provided, since it will be used when
|
||||
constructing 'next' link.
|
||||
:param name: Optional. Keep only resources with a specific name.
|
||||
:param workflow_name: Optional. Keep only resources with a specific
|
||||
|
@ -208,8 +208,9 @@ class DynamicActionsController(rest.RestController, hooks.HookController):
|
||||
)
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(resources.DynamicAction, wtypes.text, wtypes.text)
|
||||
def get(self, identifier, namespace=''):
|
||||
@wsme_pecan.wsexpose(resources.DynamicAction, wtypes.text,
|
||||
wtypes.text, types.uniquelist)
|
||||
def get(self, identifier, namespace='', fields=''):
|
||||
"""Return the named action.
|
||||
|
||||
:param identifier: Name or UUID of the action to retrieve.
|
||||
@ -222,14 +223,19 @@ class DynamicActionsController(rest.RestController, hooks.HookController):
|
||||
identifier,
|
||||
namespace
|
||||
)
|
||||
if fields and 'id' not in fields:
|
||||
fields.insert(0, 'id')
|
||||
|
||||
db_model = rest_utils.rest_retry_on_db_error(
|
||||
db_api.get_dynamic_action_definition
|
||||
)(
|
||||
identifier=identifier,
|
||||
namespace=namespace
|
||||
namespace=namespace,
|
||||
fields=fields
|
||||
)
|
||||
|
||||
if fields:
|
||||
return resources.DynamicAction.from_tuples(zip(fields, db_model))
|
||||
return resources.DynamicAction.from_db_model(db_model)
|
||||
|
||||
@rest_utils.wrap_pecan_controller_exception
|
||||
|
@ -59,7 +59,7 @@ class EnvironmentController(rest.RestController):
|
||||
or less than that of sort_keys.
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's provided, since it will be used when
|
||||
fields if it's not provided, since it will be used when
|
||||
constructing 'next' link.
|
||||
:param name: Optional. Keep only resources with a specific name.
|
||||
:param description: Optional. Keep only resources with a specific
|
||||
@ -101,21 +101,28 @@ class EnvironmentController(rest.RestController):
|
||||
)
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(resources.Environment, wtypes.text)
|
||||
def get(self, name):
|
||||
@wsme_pecan.wsexpose(resources.Environment, wtypes.text, types.uniquelist)
|
||||
def get(self, name, fields=''):
|
||||
"""Return the named environment.
|
||||
|
||||
:param name: Name of environment to retrieve
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's not provided.
|
||||
"""
|
||||
acl.enforce('environments:get', context.ctx())
|
||||
|
||||
LOG.debug("Fetch environment [name=%s]", name)
|
||||
|
||||
if fields and 'id' not in fields:
|
||||
fields.insert(0, 'id')
|
||||
|
||||
# Use retries to prevent possible failures.
|
||||
r = rest_utils.create_db_retry_object()
|
||||
db_model = r.call(db_api.get_environment, name)
|
||||
|
||||
return resources.Environment.from_db_model(db_model)
|
||||
db_model = r.call(db_api.get_environment, name, fields=fields)
|
||||
if fields:
|
||||
return resources.Environment.from_tuples(zip(fields, db_model))
|
||||
return resources.Environment.from_db_model(db_model, fields=fields)
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(
|
||||
|
@ -34,18 +34,22 @@ CREATE_MANDATORY = set(['exchange', 'topic', 'event', 'workflow_id'])
|
||||
|
||||
class EventTriggersController(rest.RestController):
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(resources.EventTrigger, types.uuid)
|
||||
def get(self, id):
|
||||
@wsme_pecan.wsexpose(resources.EventTrigger, types.uuid, types.uniquelist)
|
||||
def get(self, id, fields=''):
|
||||
"""Returns the specified event_trigger."""
|
||||
acl.enforce('event_triggers:get', auth_ctx.ctx())
|
||||
|
||||
LOG.debug('Fetch event trigger [id=%s]', id)
|
||||
|
||||
if fields and 'id' not in fields:
|
||||
fields.insert(0, 'id')
|
||||
|
||||
# Use retries to prevent possible failures.
|
||||
r = rest_utils.create_db_retry_object()
|
||||
db_model = r.call(db_api.get_event_trigger, id)
|
||||
|
||||
return resources.EventTrigger.from_db_model(db_model)
|
||||
db_model = r.call(db_api.get_event_trigger, id, fields=fields)
|
||||
if fields:
|
||||
return resources.EventTrigger.from_tuples(zip(fields, db_model))
|
||||
return resources.EventTrigger.from_db_model(db_model, fields=fields)
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(resources.EventTrigger, body=resources.EventTrigger,
|
||||
|
@ -68,12 +68,20 @@ def _get_workflow_execution_resource(wf_ex):
|
||||
|
||||
# Use retries to prevent possible failures.
|
||||
@rest_utils.rest_retry_on_db_error
|
||||
def _get_workflow_execution(id, must_exist=True):
|
||||
def _get_workflow_execution(id, must_exist=True, fields=None):
|
||||
if fields and 'id' not in fields:
|
||||
fields.insert(0, 'id')
|
||||
|
||||
fields_tuple = rest_utils.fields_list_to_cls_fields_tuple(
|
||||
db_models.WorkflowExecution,
|
||||
fields
|
||||
)
|
||||
|
||||
with db_api.transaction():
|
||||
if must_exist:
|
||||
wf_ex = db_api.get_workflow_execution(id)
|
||||
wf_ex = db_api.get_workflow_execution(id, fields=fields_tuple)
|
||||
else:
|
||||
wf_ex = db_api.load_workflow_execution(id)
|
||||
wf_ex = db_api.load_workflow_execution(id, fields=fields_tuple)
|
||||
|
||||
return rest_utils.load_deferred_fields(
|
||||
wf_ex,
|
||||
@ -90,19 +98,25 @@ class ExecutionsController(rest.RestController):
|
||||
executions = sub_execution.SubExecutionsController()
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(resources.Execution, wtypes.text)
|
||||
def get(self, id):
|
||||
@wsme_pecan.wsexpose(resources.Execution, wtypes.text, types.uniquelist)
|
||||
def get(self, id, fields=None):
|
||||
"""Return the specified Execution.
|
||||
|
||||
:param id: UUID of execution to retrieve.
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's not provided.
|
||||
"""
|
||||
acl.enforce("executions:get", context.ctx())
|
||||
|
||||
LOG.debug("Fetch execution [id=%s]", id)
|
||||
|
||||
wf_ex = _get_workflow_execution(id)
|
||||
wf_ex = _get_workflow_execution(id, fields=fields)
|
||||
|
||||
resource = resources.Execution.from_db_model(wf_ex)
|
||||
if fields:
|
||||
return resources.Execution.from_tuples(zip(fields, wf_ex))
|
||||
|
||||
resource = resources.Execution.from_db_model(wf_ex, fields=fields)
|
||||
|
||||
resource.published_global = (
|
||||
data_flow.get_workflow_execution_published_global(wf_ex)
|
||||
@ -363,7 +377,7 @@ class ExecutionsController(rest.RestController):
|
||||
or less than that of sort_keys.
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's provided, since it will be used when
|
||||
fields if it's not provided, since it will be used when
|
||||
constructing 'next' link.
|
||||
:param workflow_name: Optional. Keep only resources with a specific
|
||||
workflow name.
|
||||
|
@ -159,8 +159,8 @@ class Workflow(resource.Resource, ScopedResource):
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def from_db_model(cls, db_model):
|
||||
obj = super(Workflow, cls).from_db_model(db_model)
|
||||
def from_db_model(cls, db_model, fields=()):
|
||||
obj = super(Workflow, cls).from_db_model(db_model, fields=fields)
|
||||
|
||||
obj.set_attributes_from_spec(db_model.get('spec'))
|
||||
|
||||
|
@ -50,19 +50,22 @@ STATE_TYPES = wtypes.Enum(
|
||||
)
|
||||
|
||||
|
||||
def _get_task_resource_with_result(task_ex):
|
||||
task = resources.Task.from_db_model(task_ex)
|
||||
|
||||
task.result = json.dumps(data_flow.get_task_execution_result(task_ex))
|
||||
def _get_task_resource_with_result(task_ex, fields=()):
|
||||
task = resources.Task.from_db_model(task_ex, fields=fields)
|
||||
if 'result' in fields or not fields:
|
||||
task.result = json.dumps(data_flow.get_task_execution_result(task_ex))
|
||||
|
||||
return task
|
||||
|
||||
|
||||
# Use retries to prevent possible failures.
|
||||
@rest_utils.rest_retry_on_db_error
|
||||
def _get_task_execution(id):
|
||||
def _get_task_execution(id, fields=()):
|
||||
if fields and 'id' not in fields:
|
||||
fields.insert(0, 'id')
|
||||
|
||||
with db_api.transaction():
|
||||
task_ex = db_api.get_task_execution(id)
|
||||
task_ex = db_api.get_task_execution(id, fields=fields)
|
||||
|
||||
rest_utils.load_deferred_fields(task_ex, ['workflow_execution'])
|
||||
rest_utils.load_deferred_fields(
|
||||
@ -75,7 +78,7 @@ def _get_task_execution(id):
|
||||
['params']
|
||||
)
|
||||
|
||||
return _get_task_resource_with_result(task_ex), task_ex
|
||||
return _get_task_resource_with_result(task_ex, fields), task_ex
|
||||
|
||||
|
||||
def get_published_global(task_ex, wf_ex=None):
|
||||
@ -142,7 +145,7 @@ class TaskExecutionsController(rest.RestController):
|
||||
or less than that of sort_keys.
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's provided, since it will be used when
|
||||
fields if it's not provided, since it will be used when
|
||||
constructing 'next' link.
|
||||
:param workflow_name: Optional. Keep only resources with a specific
|
||||
workflow name.
|
||||
@ -205,18 +208,26 @@ class TasksController(rest.RestController):
|
||||
executions = sub_execution.SubExecutionsController()
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(resources.Task, wtypes.text)
|
||||
def get(self, id):
|
||||
@wsme_pecan.wsexpose(resources.Task, wtypes.text, types.uniquelist)
|
||||
def get(self, id, fields=''):
|
||||
"""Return the specified task.
|
||||
|
||||
:param id: UUID of task to retrieve
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's not provided.
|
||||
"""
|
||||
acl.enforce('tasks:get', context.ctx())
|
||||
LOG.debug("Fetch task [id=%s]", id)
|
||||
|
||||
task, task_ex = _get_task_execution(id)
|
||||
|
||||
return _task_with_published_global(task, task_ex)
|
||||
task, task_ex = _get_task_execution(id, ())
|
||||
task = _task_with_published_global(task, task_ex)
|
||||
if fields:
|
||||
if 'id' not in fields:
|
||||
fields.insert(0, 'id')
|
||||
task_dict = {field: task.to_dict()[field] for field in fields}
|
||||
task = resources.Task.from_dict(task_dict)
|
||||
return task
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(resources.Tasks, types.uuid, int, types.uniquelist,
|
||||
@ -250,7 +261,7 @@ class TasksController(rest.RestController):
|
||||
or less than that of sort_keys.
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's provided, since it will be used when
|
||||
fields if it's not provided, since it will be used when
|
||||
constructing 'next' link.
|
||||
:param name: Optional. Keep only resources with a specific name.
|
||||
:param workflow_name: Optional. Keep only resources with a specific
|
||||
@ -413,7 +424,7 @@ class ExecutionTasksController(rest.RestController):
|
||||
or less than that of sort_keys.
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's provided, since it will be used when
|
||||
fields if it's not provided, since it will be used when
|
||||
constructing 'next' link.
|
||||
:param name: Optional. Keep only resources with a specific name.
|
||||
:param workflow_name: Optional. Keep only resources with a specific
|
||||
|
@ -44,27 +44,36 @@ class WorkbooksController(rest.RestController, hooks.HookController):
|
||||
)
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(resources.Workbook, wtypes.text, wtypes.text)
|
||||
def get(self, name, namespace=''):
|
||||
@wsme_pecan.wsexpose(resources.Workbook, wtypes.text, wtypes.text,
|
||||
types.uniquelist)
|
||||
def get(self, name, namespace='', fields=''):
|
||||
"""Return the named workbook.
|
||||
|
||||
:param name: Name of workbook to retrieve.
|
||||
:param namespace: Optional. Namespace of workbook to retrieve.
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's not provided.
|
||||
"""
|
||||
acl.enforce('workbooks:get', context.ctx())
|
||||
|
||||
LOG.debug("Fetch workbook [name=%s, namespace=%s]", name, namespace)
|
||||
|
||||
if fields and 'id' not in fields:
|
||||
fields.insert(0, 'id')
|
||||
|
||||
# Use retries to prevent possible failures.
|
||||
r = rest_utils.create_db_retry_object()
|
||||
|
||||
db_model = r.call(
|
||||
db_api.get_workbook,
|
||||
name,
|
||||
namespace=namespace
|
||||
namespace=namespace,
|
||||
fields=fields
|
||||
)
|
||||
|
||||
return resources.Workbook.from_db_model(db_model)
|
||||
if fields:
|
||||
return resources.Workbook.from_tuples(zip(fields, db_model))
|
||||
return resources.Workbook.from_db_model(db_model, fields=fields)
|
||||
|
||||
@rest_utils.wrap_pecan_controller_exception
|
||||
@pecan.expose(content_type="text/plain")
|
||||
@ -171,7 +180,7 @@ class WorkbooksController(rest.RestController, hooks.HookController):
|
||||
Default: asc.
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's provided, since it will be used when
|
||||
fields if it's not provided, since it will be used when
|
||||
constructing 'next' link.
|
||||
:param name: Optional. Keep only resources with a specific name.
|
||||
:param definition: Optional. Keep only resources with a specific
|
||||
|
@ -77,26 +77,36 @@ class WorkflowsController(rest.RestController, hooks.HookController):
|
||||
)
|
||||
|
||||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(resources.Workflow, wtypes.text, wtypes.text)
|
||||
def get(self, identifier, namespace=''):
|
||||
@wsme_pecan.wsexpose(resources.Workflow, wtypes.text, wtypes.text,
|
||||
types.uniquelist)
|
||||
def get(self, identifier, namespace='', fields=''):
|
||||
"""Return the named workflow.
|
||||
|
||||
:param identifier: Name or UUID of the workflow to retrieve.
|
||||
:param namespace: Optional. Namespace of the workflow to retrieve.
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's not provided.
|
||||
"""
|
||||
acl.enforce('workflows:get', context.ctx())
|
||||
|
||||
LOG.debug("Fetch workflow [identifier=%s]", identifier)
|
||||
|
||||
if fields and 'id' not in fields:
|
||||
fields.insert(0, 'id')
|
||||
|
||||
# Use retries to prevent possible failures.
|
||||
r = rest_utils.create_db_retry_object()
|
||||
db_model = r.call(
|
||||
db_api.get_workflow_definition,
|
||||
identifier,
|
||||
namespace=namespace
|
||||
namespace=namespace,
|
||||
fields=fields,
|
||||
)
|
||||
|
||||
return resources.Workflow.from_db_model(db_model)
|
||||
if fields:
|
||||
return resources.Workflow.from_tuples(zip(fields, db_model))
|
||||
return resources.Workflow.from_db_model(db_model, fields=fields)
|
||||
|
||||
@rest_utils.wrap_pecan_controller_exception
|
||||
@pecan.expose(content_type="text/plain")
|
||||
@ -240,7 +250,7 @@ class WorkflowsController(rest.RestController, hooks.HookController):
|
||||
Default: asc.
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's provided, since it will be used when
|
||||
fields if it's not provided, since it will be used when
|
||||
constructing 'next' link.
|
||||
:param name: Optional. Keep only resources with a specific name.
|
||||
:param namespace: Optional. Keep only resources with a specific
|
||||
|
@ -87,15 +87,18 @@ class _MistralModelBase(oslo_models.ModelBase, oslo_models.TimestampMixin):
|
||||
if col.name not in unloaded and hasattr(self, col.name):
|
||||
yield col.name
|
||||
|
||||
def iter_columns(self):
|
||||
def iter_columns(self, fields=()):
|
||||
"""Returns an iterator for loaded columns.
|
||||
|
||||
:param fields: names of fields to return
|
||||
:type fields: tuple, list or set
|
||||
:return: A generator function that generates
|
||||
tuples (column name, column value).
|
||||
"""
|
||||
|
||||
for col_name in self.iter_column_names():
|
||||
yield col_name, getattr(self, col_name)
|
||||
if not fields or col_name in fields:
|
||||
yield col_name, getattr(self, col_name)
|
||||
|
||||
def get_clone(self):
|
||||
"""Clones current object, loads all fields and returns the result."""
|
||||
|
@ -524,17 +524,17 @@ def get_scheduled_jobs_count(**kwargs):
|
||||
|
||||
# Cron triggers.
|
||||
|
||||
def get_cron_trigger(identifier):
|
||||
return IMPL.get_cron_trigger(identifier)
|
||||
def get_cron_trigger(identifier, fields=()):
|
||||
return IMPL.get_cron_trigger(identifier, fields=fields)
|
||||
|
||||
|
||||
def get_cron_trigger_by_id(id):
|
||||
return IMPL.get_cron_trigger_by_id(id)
|
||||
def get_cron_trigger_by_id(id, fields=()):
|
||||
return IMPL.get_cron_trigger_by_id(id, fields=fields)
|
||||
|
||||
|
||||
def load_cron_trigger(identifier):
|
||||
def load_cron_trigger(identifier, fields=()):
|
||||
"""Unlike get_cron_trigger this method is allowed to return None."""
|
||||
return IMPL.load_cron_trigger(identifier)
|
||||
return IMPL.load_cron_trigger(identifier, fields=fields)
|
||||
|
||||
|
||||
def get_cron_triggers(**kwargs):
|
||||
@ -593,13 +593,13 @@ def delete_cron_triggers(**kwargs):
|
||||
|
||||
# Environments.
|
||||
|
||||
def get_environment(name):
|
||||
return IMPL.get_environment(name)
|
||||
def get_environment(name, fields=()):
|
||||
return IMPL.get_environment(name, fields=fields)
|
||||
|
||||
|
||||
def load_environment(name):
|
||||
def load_environment(name, fields=()):
|
||||
"""Unlike get_environment this method is allowed to return None."""
|
||||
return IMPL.load_environment(name)
|
||||
return IMPL.load_environment(name, fields=fields)
|
||||
|
||||
|
||||
def get_environments(limit=None, marker=None, sort_keys=None,
|
||||
@ -667,12 +667,12 @@ def delete_resource_members(**kwargs):
|
||||
|
||||
# Event triggers.
|
||||
|
||||
def get_event_trigger(id, insecure=False):
|
||||
return IMPL.get_event_trigger(id, insecure)
|
||||
def get_event_trigger(id, insecure=False, fields=()):
|
||||
return IMPL.get_event_trigger(id, insecure, fields=fields)
|
||||
|
||||
|
||||
def load_event_trigger(id, insecure=False):
|
||||
return IMPL.load_event_trigger(id, insecure)
|
||||
def load_event_trigger(id, insecure=False, fields=()):
|
||||
return IMPL.load_event_trigger(id, insecure, fields=fields)
|
||||
|
||||
|
||||
def get_event_triggers(insecure=False, limit=None, marker=None, sort_keys=None,
|
||||
|
@ -307,6 +307,11 @@ def _get_db_object_by_name(model, name, columns=()):
|
||||
|
||||
|
||||
def _get_db_object_by_id(model, id, insecure=False, columns=()):
|
||||
columns = (
|
||||
tuple([getattr(model, f) for f in columns if hasattr(model, f)])
|
||||
if columns and isinstance(columns, list) else columns
|
||||
)
|
||||
|
||||
query = (
|
||||
b.model_query(model, columns=columns)
|
||||
if insecure
|
||||
@ -319,6 +324,10 @@ def _get_db_object_by_id(model, id, insecure=False, columns=()):
|
||||
def _get_db_object_by_name_and_namespace_or_id(model, identifier,
|
||||
namespace=None, insecure=False,
|
||||
columns=()):
|
||||
columns = (
|
||||
tuple([getattr(model, f) for f in columns if hasattr(model, f)])
|
||||
if columns and isinstance(columns, list) else columns
|
||||
)
|
||||
query = (
|
||||
b.model_query(model, columns=columns)
|
||||
if insecure
|
||||
@ -1599,13 +1608,14 @@ def _get_completed_root_executions_query(columns):
|
||||
|
||||
|
||||
@b.session_aware()
|
||||
def get_cron_trigger(identifier, session=None):
|
||||
def get_cron_trigger(identifier, session=None, fields=()):
|
||||
ctx = context.ctx()
|
||||
|
||||
cron_trigger = _get_db_object_by_name_and_namespace_or_id(
|
||||
models.CronTrigger,
|
||||
identifier,
|
||||
insecure=ctx.is_admin
|
||||
insecure=ctx.is_admin,
|
||||
columns=fields,
|
||||
)
|
||||
|
||||
if not cron_trigger:
|
||||
@ -1617,10 +1627,11 @@ def get_cron_trigger(identifier, session=None):
|
||||
|
||||
|
||||
@b.session_aware()
|
||||
def get_cron_trigger_by_id(id, session=None):
|
||||
def get_cron_trigger_by_id(id, session=None, fields=()):
|
||||
ctx = context.ctx()
|
||||
cron_trigger = _get_db_object_by_id(models.CronTrigger, id,
|
||||
insecure=ctx.is_admin)
|
||||
insecure=ctx.is_admin,
|
||||
columns=fields)
|
||||
if not cron_trigger:
|
||||
raise exc.DBEntityNotFoundError(
|
||||
"Cron trigger not found [id=%s]" % id
|
||||
@ -1630,10 +1641,11 @@ def get_cron_trigger_by_id(id, session=None):
|
||||
|
||||
|
||||
@b.session_aware()
|
||||
def load_cron_trigger(identifier, session=None):
|
||||
def load_cron_trigger(identifier, session=None, fields=()):
|
||||
return _get_db_object_by_name_and_namespace_or_id(
|
||||
models.CronTrigger,
|
||||
identifier
|
||||
identifier,
|
||||
columns=fields,
|
||||
)
|
||||
|
||||
|
||||
@ -1744,8 +1756,8 @@ def delete_cron_triggers(session=None, **kwargs):
|
||||
# Environments.
|
||||
|
||||
@b.session_aware()
|
||||
def get_environment(name, session=None):
|
||||
env = _get_db_object_by_name(models.Environment, name)
|
||||
def get_environment(name, session=None, fields=()):
|
||||
env = _get_db_object_by_name(models.Environment, name, columns=fields)
|
||||
|
||||
if not env:
|
||||
raise exc.DBEntityNotFoundError(
|
||||
@ -1756,8 +1768,8 @@ def get_environment(name, session=None):
|
||||
|
||||
|
||||
@b.session_aware()
|
||||
def load_environment(name, session=None):
|
||||
return _get_db_object_by_name(models.Environment, name)
|
||||
def load_environment(name, session=None, fields=()):
|
||||
return _get_db_object_by_name(models.Environment, name, columns=fields)
|
||||
|
||||
|
||||
@b.session_aware()
|
||||
@ -1982,8 +1994,9 @@ def _get_accepted_resources(res_type):
|
||||
# Event triggers.
|
||||
|
||||
@b.session_aware()
|
||||
def get_event_trigger(id, insecure=False, session=None):
|
||||
event_trigger = _get_db_object_by_id(models.EventTrigger, id, insecure)
|
||||
def get_event_trigger(id, insecure=False, session=None, fields=()):
|
||||
event_trigger = _get_db_object_by_id(models.EventTrigger, id, insecure,
|
||||
columns=fields)
|
||||
|
||||
if not event_trigger:
|
||||
raise exc.DBEntityNotFoundError(
|
||||
@ -1994,8 +2007,9 @@ def get_event_trigger(id, insecure=False, session=None):
|
||||
|
||||
|
||||
@b.session_aware()
|
||||
def load_event_trigger(id, insecure=False, session=None):
|
||||
return _get_db_object_by_id(models.EventTrigger, id, insecure)
|
||||
def load_event_trigger(id, insecure=False, session=None, fields=()):
|
||||
return _get_db_object_by_id(models.EventTrigger, id, insecure,
|
||||
columns=fields)
|
||||
|
||||
|
||||
@b.session_aware()
|
||||
|
@ -226,9 +226,22 @@ class TestActionExecutionsController(base.APITest):
|
||||
@mock.patch.object(db_api, 'get_action_execution', MOCK_ACTION)
|
||||
def test_get(self):
|
||||
resp = self.app.get('/v2/action_executions/123')
|
||||
action_exec = copy.deepcopy(ACTION_EX)
|
||||
del action_exec['task_name']
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertDictEqual(action_exec, resp.json)
|
||||
|
||||
@mock.patch('mistral.db.v2.api.get_action_execution')
|
||||
def test_get_with_fields_filter(self, mocked_get):
|
||||
mocked_get.return_value = (ACTION_EX['id'], ACTION_EX['name'],)
|
||||
resp = self.app.get('/v2/action_executions/123?fields=name')
|
||||
expected = {
|
||||
'id': ACTION_EX['id'],
|
||||
'name': ACTION_EX['name'],
|
||||
}
|
||||
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertDictEqual(ACTION_EX, resp.json)
|
||||
self.assertDictEqual(expected, resp.json)
|
||||
|
||||
@mock.patch.object(db_api, 'get_action_execution')
|
||||
def test_get_operational_error(self, mocked_get):
|
||||
@ -239,9 +252,10 @@ class TestActionExecutionsController(base.APITest):
|
||||
]
|
||||
|
||||
resp = self.app.get('/v2/action_executions/123')
|
||||
|
||||
action_exec = copy.deepcopy(ACTION_EX)
|
||||
del action_exec['task_name']
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertDictEqual(ACTION_EX, resp.json)
|
||||
self.assertDictEqual(action_exec, resp.json)
|
||||
|
||||
def test_basic_get(self):
|
||||
resp = self.app.get('/v2/action_executions/')
|
||||
@ -259,7 +273,7 @@ class TestActionExecutionsController(base.APITest):
|
||||
resp = self.app.get('/v2/action_executions/123')
|
||||
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertTrue('project_id' in resp.json)
|
||||
self.assertIn('project_id', resp.json)
|
||||
|
||||
@mock.patch.object(oslo_client.OsloRPCClient, 'sync_call',
|
||||
mock.MagicMock(side_effect=oslo_exc.MessagingTimeout))
|
||||
@ -561,11 +575,12 @@ class TestActionExecutionsController(base.APITest):
|
||||
@mock.patch.object(db_api, 'get_action_executions', MOCK_ACTIONS)
|
||||
def test_get_all(self):
|
||||
resp = self.app.get('/v2/action_executions')
|
||||
|
||||
action_exec = copy.deepcopy(ACTION_EX)
|
||||
del action_exec['task_name']
|
||||
self.assertEqual(200, resp.status_int)
|
||||
|
||||
self.assertEqual(1, len(resp.json['action_executions']))
|
||||
self.assertDictEqual(ACTION_EX, resp.json['action_executions'][0])
|
||||
self.assertDictEqual(action_exec, resp.json['action_executions'][0])
|
||||
|
||||
@mock.patch.object(db_api, 'get_action_executions')
|
||||
def test_get_all_operational_error(self, mocked_get_all):
|
||||
@ -576,11 +591,13 @@ class TestActionExecutionsController(base.APITest):
|
||||
]
|
||||
|
||||
resp = self.app.get('/v2/action_executions')
|
||||
action_exec = copy.deepcopy(ACTION_EX)
|
||||
del action_exec['task_name']
|
||||
|
||||
self.assertEqual(200, resp.status_int)
|
||||
|
||||
self.assertEqual(1, len(resp.json['action_executions']))
|
||||
self.assertDictEqual(ACTION_EX, resp.json['action_executions'][0])
|
||||
self.assertDictEqual(action_exec, resp.json['action_executions'][0])
|
||||
|
||||
@mock.patch.object(rest_utils, 'get_all',
|
||||
return_value=resources.ActionExecutions())
|
||||
|
@ -79,6 +79,18 @@ class TestCronTriggerController(base.APITest):
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertDictEqual(TRIGGER, resp.json)
|
||||
|
||||
@mock.patch('mistral.db.v2.api.get_cron_trigger')
|
||||
def test_get_with_fields_filter(self, mocked_get):
|
||||
mocked_get.return_value = (TRIGGER['id'], TRIGGER['name'],)
|
||||
resp = self.app.get('/v2/cron_triggers/my_cron_trigger?fields=name')
|
||||
expected = {
|
||||
'id': TRIGGER['id'],
|
||||
'name': TRIGGER['name'],
|
||||
}
|
||||
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertDictEqual(expected, resp.json)
|
||||
|
||||
@mock.patch.object(db_api, 'get_cron_trigger')
|
||||
def test_get_operational_error(self, mocked_get):
|
||||
mocked_get.side_effect = [
|
||||
|
@ -188,6 +188,20 @@ class TestEnvironmentController(base.APITest):
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self._assert_dict_equal(ENVIRONMENT, resp.json)
|
||||
|
||||
@mock.patch('mistral.db.v2.api.get_environment')
|
||||
def test_get_with_fields_filter(self, mocked_get):
|
||||
mocked_get.return_value = (
|
||||
ENVIRONMENT['id'], ENVIRONMENT['name'],
|
||||
)
|
||||
resp = self.app.get('/v2/environments/123?fields=name')
|
||||
expected = {
|
||||
'id': ENVIRONMENT['id'],
|
||||
'name': ENVIRONMENT['name'],
|
||||
}
|
||||
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertDictEqual(expected, resp.json)
|
||||
|
||||
@mock.patch.object(db_api, 'get_environment')
|
||||
def test_get_operational_error(self, mocked_get):
|
||||
mocked_get.side_effect = [
|
||||
|
@ -77,6 +77,21 @@ class TestEventTriggerController(base.APITest):
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertDictEqual(TRIGGER, resp.json)
|
||||
|
||||
@mock.patch('mistral.db.v2.api.get_event_trigger')
|
||||
def test_get_with_fields_filter(self, mocked_get):
|
||||
mocked_get.return_value = (TRIGGER['id'], TRIGGER['name'],)
|
||||
resp = self.app.get(
|
||||
'/v2/event_triggers/09cc56a9-d15e-4494-a6e2-c4ec8bdaacae'
|
||||
'?fields=name'
|
||||
)
|
||||
expected = {
|
||||
'id': TRIGGER['id'],
|
||||
'name': TRIGGER['name'],
|
||||
}
|
||||
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertDictEqual(expected, resp.json)
|
||||
|
||||
@mock.patch.object(db_api, 'get_event_trigger')
|
||||
def test_get_operational_error(self, mocked_get):
|
||||
mocked_get.side_effect = [
|
||||
|
@ -169,6 +169,20 @@ class TestExecutionsController(base.APITest):
|
||||
|
||||
self.assertDictEqual(expected, resp.json)
|
||||
|
||||
@mock.patch('mistral.db.v2.api.get_workflow_execution')
|
||||
def test_get_with_fields_filter(self, mocked_get):
|
||||
mocked_get.return_value = (
|
||||
WF_EX_JSON_WITH_DESC['id'], WF_EX_JSON_WITH_DESC['description'],
|
||||
)
|
||||
resp = self.app.get('/v2/executions/123?fields=description')
|
||||
expected = {
|
||||
'id': WF_EX_JSON_WITH_DESC['id'],
|
||||
'description': WF_EX_JSON_WITH_DESC['description'],
|
||||
}
|
||||
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertDictEqual(expected, resp.json)
|
||||
|
||||
@mock.patch.object(db_api, 'get_workflow_execution')
|
||||
def test_get_operational_error(self, mocked_get):
|
||||
mocked_get.side_effect = [
|
||||
@ -564,7 +578,8 @@ class TestExecutionsController(base.APITest):
|
||||
self.assertEqual(201, resp.status_int)
|
||||
self.assertDictEqual(expected_json, resp.json)
|
||||
|
||||
load_wf_ex_func.assert_called_once_with(expected_json['id'])
|
||||
load_wf_ex_func.assert_called_once_with(expected_json['id'],
|
||||
fields=())
|
||||
|
||||
kwargs = json.loads(expected_json['params'])
|
||||
kwargs['description'] = expected_json['description']
|
||||
@ -600,7 +615,8 @@ class TestExecutionsController(base.APITest):
|
||||
self.assertEqual(201, resp.status_int)
|
||||
self.assertDictEqual(expected_json, resp.json)
|
||||
|
||||
load_wf_ex_func.assert_called_once_with(expected_json['id'])
|
||||
load_wf_ex_func.assert_called_once_with(expected_json['id'],
|
||||
fields=())
|
||||
|
||||
# Note that "start_workflow" method on engine API should not be called
|
||||
# in this case because we passed execution ID to the endpoint and the
|
||||
|
@ -171,6 +171,18 @@ class TestTasksController(base.APITest):
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertDictEqual(TASK, resp.json)
|
||||
|
||||
@mock.patch('mistral.db.v2.api.get_task_execution')
|
||||
def test_get_with_fields_filter(self, mocked_get):
|
||||
mocked_get.return_value = TASK_EX
|
||||
resp = self.app.get('/v2/tasks/123?fields=name')
|
||||
expected = {
|
||||
'id': TASK['id'],
|
||||
'name': TASK['name'],
|
||||
}
|
||||
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertDictEqual(expected, resp.json)
|
||||
|
||||
@mock.patch.object(db_api, 'get_task_execution')
|
||||
def test_get_operational_error(self, mocked_get):
|
||||
mocked_get.side_effect = [
|
||||
|
@ -169,6 +169,18 @@ class TestWorkbooksController(base.APITest):
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertDictEqual(WORKBOOK, resp.json)
|
||||
|
||||
@mock.patch('mistral.db.v2.api.get_workbook')
|
||||
def test_get_with_fields_filter(self, mocked_get):
|
||||
mocked_get.return_value = (WORKBOOK['id'], WORKBOOK['name'],)
|
||||
resp = self.app.get('/v2/workbooks/123?fields=name')
|
||||
expected = {
|
||||
'id': WORKBOOK['id'],
|
||||
'name': WORKBOOK['name'],
|
||||
}
|
||||
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertDictEqual(expected, resp.json)
|
||||
|
||||
@mock.patch.object(db_api, "get_workbook", MOCK_WB_WITH_NAMESPACE)
|
||||
def test_get_with_namespace(self):
|
||||
resp = self.app.get('/v2/workbooks/123?namespace=xyz')
|
||||
|
@ -264,6 +264,18 @@ class TestWorkflowsController(base.APITest):
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertDictEqual(WF, resp_json)
|
||||
|
||||
@mock.patch('mistral.db.v2.api.get_workflow_definition')
|
||||
def test_get_with_fields_filter(self, mocked_get):
|
||||
mocked_get.return_value = (WF['id'], WF['name'],)
|
||||
resp = self.app.get('/v2/workflows/123?fields=name')
|
||||
expected = {
|
||||
'id': WF['id'],
|
||||
'name': WF['name'],
|
||||
}
|
||||
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertDictEqual(expected, resp.json)
|
||||
|
||||
@mock.patch.object(db_api, 'get_workflow_definition')
|
||||
def test_get_operational_error(self, mocked_get):
|
||||
mocked_get.side_effect = [
|
||||
@ -886,4 +898,4 @@ class TestWorkflowsController(base.APITest):
|
||||
resp = self.app.get(
|
||||
'/v2/workflows/123e4567-e89b-12d3-a456-426655440000')
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertTrue('project_id' in resp.json)
|
||||
self.assertIn('project_id', resp.json)
|
||||
|
@ -123,6 +123,14 @@ def validate_fields(fields, object_fields):
|
||||
)
|
||||
|
||||
|
||||
def fields_list_to_cls_fields_tuple(model, f):
|
||||
if not f:
|
||||
return ()
|
||||
return tuple(
|
||||
[getattr(model, str(field)) for field in f]
|
||||
)
|
||||
|
||||
|
||||
def filters_to_dict(**kwargs):
|
||||
"""Return only non-null values
|
||||
|
||||
@ -159,7 +167,7 @@ def get_all(list_cls, cls, get_all_function, get_function,
|
||||
Default: ['asc'].
|
||||
:param fields: Optional. A specified list of fields of the resource to
|
||||
be returned. 'id' will be included automatically in
|
||||
fields if it's provided, since it will be used when
|
||||
fields if it's not provided, since it will be used when
|
||||
constructing 'next' link.
|
||||
:param filters: Optional. A specified dictionary of filters to match.
|
||||
:param all_projects: Optional. Get resources of all projects.
|
||||
|
Loading…
x
Reference in New Issue
Block a user