Code improvements after the dynamic actions patch
* Style improvements to make sure the code is compliant with the coding guidelines * Fixing small but important things like the mismatch between the sinatures of the methods find_all() in the DynamicActionProvider class and the base ActionProvider interface. * Improved the tests. * Simplified the implementation of DynamicActionProvider. Change-Id: Idbfb15b4c3bb415e7fa9c7ece27eabfe674b6059
This commit is contained in:
parent
9be4f8e119
commit
f78f33507e
|
@ -13,24 +13,22 @@
|
|||
# limitations under the License.
|
||||
|
||||
|
||||
import collections
|
||||
from oslo_config import cfg
|
||||
import types
|
||||
|
||||
from mistral import exceptions as exc
|
||||
from mistral_lib import actions as ml_actions
|
||||
from mistral_lib import serialization
|
||||
from mistral_lib.utils import inspect_utils
|
||||
|
||||
from mistral.db.v2 import api as db_api
|
||||
from mistral.services import code_sources as code_sources_service
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class DynamicAction(ml_actions.Action):
|
||||
|
||||
def __init__(self, action, code_source_id, namespace=''):
|
||||
super(DynamicAction, self).__init__()
|
||||
|
||||
self.action = action
|
||||
self.namespace = namespace
|
||||
self.code_source_id = code_source_id
|
||||
|
@ -47,7 +45,7 @@ class DynamicAction(ml_actions.Action):
|
|||
|
||||
|
||||
class DynamicActionDescriptor(ml_actions.PythonActionDescriptor):
|
||||
def __init__(self, name, cls_name, action_cls, version, code_source_id,
|
||||
def __init__(self, name, cls_name, action_cls, code_source_id, version,
|
||||
action_cls_attrs=None, namespace='', project_id=None,
|
||||
scope=None):
|
||||
super(DynamicActionDescriptor, self).__init__(
|
||||
|
@ -60,8 +58,8 @@ class DynamicActionDescriptor(ml_actions.PythonActionDescriptor):
|
|||
)
|
||||
|
||||
self.cls_name = cls_name
|
||||
self.version = version
|
||||
self.code_source_id = code_source_id
|
||||
self.version = version
|
||||
|
||||
def __repr__(self):
|
||||
return 'Dynamic action [name=%s, cls=%s , code_source_id=%s,' \
|
||||
|
@ -78,7 +76,8 @@ class DynamicActionDescriptor(ml_actions.PythonActionDescriptor):
|
|||
return DynamicAction(
|
||||
self._action_cls(**params),
|
||||
self.code_source_id,
|
||||
self.namespace)
|
||||
self.namespace
|
||||
)
|
||||
|
||||
dynamic_cls = type(
|
||||
self._action_cls.__name__,
|
||||
|
@ -108,7 +107,7 @@ class DynamicActionSerializer(serialization.DictBasedSerializer):
|
|||
def deserialize_from_dict(self, entity_dict):
|
||||
cls_name = entity_dict['cls_name']
|
||||
|
||||
mod = _get_module(
|
||||
mod = _get_python_module(
|
||||
entity_dict['code_source_id'],
|
||||
entity_dict['namespace']
|
||||
)
|
||||
|
@ -132,18 +131,18 @@ class DynamicActionSerializer(serialization.DictBasedSerializer):
|
|||
)
|
||||
|
||||
|
||||
def _get_module(code_source_id, namespace=''):
|
||||
code_source = code_sources_service.get_code_source(
|
||||
def _get_python_module(code_source_id, namespace=''):
|
||||
code_source = db_api.get_code_source(
|
||||
code_source_id,
|
||||
namespace
|
||||
namespace=namespace
|
||||
)
|
||||
|
||||
mod = _load_module(code_source.name, code_source.src)
|
||||
mod = _load_python_module(code_source.name, code_source.src)
|
||||
|
||||
return mod, code_source.version
|
||||
|
||||
|
||||
def _load_module(fullname, content):
|
||||
def _load_python_module(fullname, content):
|
||||
mod = types.ModuleType(fullname)
|
||||
|
||||
exec(content, mod.__dict__)
|
||||
|
@ -160,157 +159,73 @@ class DynamicActionProvider(ml_actions.ActionProvider):
|
|||
def __init__(self, name='dynamic'):
|
||||
super().__init__(name)
|
||||
|
||||
self._action_descs = collections.OrderedDict()
|
||||
self._code_sources = collections.OrderedDict()
|
||||
# {code_source_id => (python module, version)}
|
||||
self._code_sources = dict()
|
||||
|
||||
def _get_code_source_version(self, code_src_id, namespace=''):
|
||||
code_src = code_sources_service.get_code_source(
|
||||
code_src_id,
|
||||
namespace,
|
||||
fields=['version']
|
||||
)
|
||||
def ensure_latest_module_version(self, action_def):
|
||||
# We need to compare the version of the corresponding module
|
||||
# that's already loaded into memory with the version stored in
|
||||
# DB and reimport the module if the DB version is higher.
|
||||
|
||||
return code_src[0]
|
||||
code_src_id = action_def.code_source_id
|
||||
|
||||
def _load_code_source(self, id):
|
||||
mod_pair = _get_module(id)
|
||||
self._code_sources[id] = mod_pair
|
||||
# TODO(rakhmerov): To avoid this DB call we need to store code source
|
||||
# versions also in the dynamic action definition model.
|
||||
db_ver = db_api.get_code_source(code_src_id, fields=['version'])[0]
|
||||
|
||||
return mod_pair
|
||||
if db_ver > self._code_sources.get(code_src_id, (None, -1))[1]:
|
||||
# Reload module.
|
||||
code_src = db_api.get_code_source(code_src_id)
|
||||
|
||||
def _get_code_source(self, id):
|
||||
mod_pair = self._code_sources.get(id)
|
||||
code_src_db_version = self._get_code_source_version(id)
|
||||
module = _load_python_module(code_src.name, code_src.src)
|
||||
|
||||
if not mod_pair or mod_pair[1] != code_src_db_version:
|
||||
mod_pair = self._load_code_source(id)
|
||||
|
||||
return mod_pair
|
||||
|
||||
def _get_action_from_db(self, name, namespace, fields=()):
|
||||
action = None
|
||||
|
||||
try:
|
||||
action = db_api.get_dynamic_action(
|
||||
identifier=name,
|
||||
namespace=namespace,
|
||||
fields=fields
|
||||
)
|
||||
except exc.DBEntityNotFoundError:
|
||||
pass
|
||||
|
||||
return action
|
||||
|
||||
def _action_exists_in_db(self, name, namespace):
|
||||
action = self._get_action_from_db(
|
||||
name,
|
||||
namespace,
|
||||
fields=['name']
|
||||
)
|
||||
|
||||
return action is not None
|
||||
|
||||
def _reload_action(self, action_desc, mod_pair):
|
||||
action_desc._action_cls = getattr(
|
||||
mod_pair[0],
|
||||
action_desc.cls_name
|
||||
)
|
||||
|
||||
action_desc.version = mod_pair[1]
|
||||
|
||||
def _load_new_action(self, action_name, namespace, action_def):
|
||||
# only query the db if action_def was None
|
||||
action_def = action_def or self._get_action_from_db(
|
||||
action_name,
|
||||
namespace=namespace
|
||||
)
|
||||
|
||||
if not action_def:
|
||||
return
|
||||
|
||||
mod_pair = self._get_code_source(action_def.code_source_id)
|
||||
|
||||
cls = getattr(mod_pair[0], action_def.class_name)
|
||||
|
||||
action_desc = DynamicActionDescriptor(
|
||||
name=action_def.name,
|
||||
action_cls=cls,
|
||||
cls_name=action_def.class_name,
|
||||
version=1,
|
||||
code_source_id=action_def.code_source_id
|
||||
)
|
||||
|
||||
self._action_descs[(action_name, namespace)] = action_desc
|
||||
|
||||
return action_desc
|
||||
|
||||
def _load_existing_action(self, action_desc, action_name, namespace):
|
||||
if not self._action_exists_in_db(action_name, namespace=namespace):
|
||||
# deleting action from cache
|
||||
del self._action_descs[(action_name, namespace)]
|
||||
|
||||
return
|
||||
|
||||
mod_pair = self._get_code_source(action_desc.code_source_id)
|
||||
|
||||
if action_desc.version != mod_pair[1]:
|
||||
self._reload_action(action_desc, mod_pair)
|
||||
|
||||
return action_desc
|
||||
|
||||
def _load_action(self, action_name, namespace=None, action_def=None):
|
||||
action_desc = self._action_descs.get((action_name, namespace))
|
||||
|
||||
if action_desc:
|
||||
action_desc = self._load_existing_action(
|
||||
action_desc,
|
||||
action_name,
|
||||
namespace
|
||||
)
|
||||
self._code_sources[code_src_id] = (module, code_src.version)
|
||||
else:
|
||||
action_desc = self._load_new_action(
|
||||
action_name,
|
||||
namespace,
|
||||
action_def
|
||||
)
|
||||
module = self._code_sources[code_src_id][0]
|
||||
|
||||
return action_desc
|
||||
return module
|
||||
|
||||
def _get_action_class(self, action_def):
|
||||
module = self.ensure_latest_module_version(action_def)
|
||||
|
||||
return getattr(module, action_def.class_name)
|
||||
|
||||
def _build_action_descriptor(self, action_def):
|
||||
action_cls = self._get_action_class(action_def)
|
||||
|
||||
return DynamicActionDescriptor(
|
||||
name=action_def.name,
|
||||
cls_name=action_def.class_name,
|
||||
action_cls=action_cls,
|
||||
code_source_id=action_def.code_source_id,
|
||||
version=1,
|
||||
project_id=action_def.project_id,
|
||||
scope=action_def.scope
|
||||
)
|
||||
|
||||
def find(self, action_name, namespace=None):
|
||||
action_def = db_api.load_dynamic_action_definition(
|
||||
action_name,
|
||||
namespace
|
||||
)
|
||||
|
||||
return self._load_action(action_name, namespace)
|
||||
if action_def is None:
|
||||
return None
|
||||
|
||||
def _clean_deleted_actions_from_cache(self):
|
||||
to_delete = [
|
||||
key for key in self._action_descs.keys()
|
||||
if not self._action_exists_in_db(*key)
|
||||
]
|
||||
|
||||
for key in to_delete:
|
||||
del self._action_descs[key]
|
||||
return self._build_action_descriptor(action_def)
|
||||
|
||||
def find_all(self, namespace='', limit=None, sort_fields=None,
|
||||
sort_dirs=None, **filters):
|
||||
filters = {
|
||||
'namespace': {'eq': namespace}
|
||||
}
|
||||
self._clean_deleted_actions_from_cache()
|
||||
sort_dirs=None, filters=None):
|
||||
if filters is None:
|
||||
filters = dict()
|
||||
|
||||
actions = db_api.get_dynamic_actions(
|
||||
filters['namespace'] = {'eq': namespace}
|
||||
|
||||
action_defs = db_api.get_dynamic_action_definitions(
|
||||
limit=limit,
|
||||
sort_keys=sort_fields,
|
||||
sort_dirs=sort_dirs,
|
||||
**filters
|
||||
)
|
||||
|
||||
for action in actions:
|
||||
self._load_action(
|
||||
action.name,
|
||||
namespace=namespace,
|
||||
action_def=action
|
||||
)
|
||||
|
||||
return dict(filter(
|
||||
lambda elem: elem[0][1] == namespace,
|
||||
self._action_descs.items())
|
||||
)
|
||||
return [self._build_action_descriptor(a_d) for a_d in action_defs]
|
||||
|
|
|
@ -42,19 +42,19 @@ class DynamicActionsController(rest.RestController, hooks.HookController):
|
|||
@rest_utils.wrap_pecan_controller_exception
|
||||
@pecan.expose(content_type="text/plain")
|
||||
def post(self, namespace=''):
|
||||
"""Creates new Actions.
|
||||
"""Creates new dynamic actions.
|
||||
|
||||
:param namespace: Optional. The namespace to create the actions in.
|
||||
|
||||
The text is allowed to have multiple Actions, In such case,
|
||||
they all will be created.
|
||||
The text is allowed to have multiple actions. In such case, they all
|
||||
will be created.
|
||||
"""
|
||||
acl.enforce('dynamic_actions:create', context.ctx())
|
||||
|
||||
actions = safe_yaml.load(pecan.request.text)
|
||||
|
||||
LOG.debug(
|
||||
'Creating Actions with names: %s in namespace:[%s]',
|
||||
'Creating dynamic actions with names: %s in namespace:[%s]',
|
||||
actions,
|
||||
namespace
|
||||
)
|
||||
|
@ -66,8 +66,7 @@ class DynamicActionsController(rest.RestController, hooks.HookController):
|
|||
for action in actions_db
|
||||
]
|
||||
|
||||
return resources.DynamicActions(
|
||||
dynamic_actions=actions_list).to_json()
|
||||
return resources.DynamicActions(dynamic_actions=actions_list).to_json()
|
||||
|
||||
@wsme_pecan.wsexpose(resources.DynamicActions, types.uuid, int,
|
||||
types.uniquelist, types.list, types.uniquelist,
|
||||
|
@ -138,8 +137,8 @@ class DynamicActionsController(rest.RestController, hooks.HookController):
|
|||
return rest_utils.get_all(
|
||||
resources.DynamicActions,
|
||||
resources.DynamicAction,
|
||||
db_api.get_dynamic_actions,
|
||||
db_api.get_dynamic_action,
|
||||
db_api.get_dynamic_action_definitions,
|
||||
db_api.get_dynamic_action_definition,
|
||||
marker=marker,
|
||||
limit=limit,
|
||||
sort_keys=sort_keys,
|
||||
|
@ -223,5 +222,4 @@ class DynamicActionsController(rest.RestController, hooks.HookController):
|
|||
for action in actions_db
|
||||
]
|
||||
|
||||
return resources.DynamicActions(
|
||||
dynamic_actions=actions_list).to_json()
|
||||
return resources.DynamicActions(dynamic_actions=actions_list).to_json()
|
||||
|
|
|
@ -53,7 +53,7 @@ def upgrade():
|
|||
)
|
||||
|
||||
op.create_table(
|
||||
'dynamic_actions',
|
||||
'dynamic_action_definitions',
|
||||
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
|
@ -74,7 +74,7 @@ def upgrade():
|
|||
|
||||
sa.UniqueConstraint('name', 'namespace', 'project_id'),
|
||||
|
||||
sa.Index('dynamic_actions_project_id', 'project_id'),
|
||||
sa.Index('dynamic_actions_scope', 'scope'),
|
||||
sa.Index('dynamic_action_definitions_project_id', 'project_id'),
|
||||
sa.Index('dynamic_action_definitions_scope', 'scope'),
|
||||
|
||||
)
|
|
@ -170,25 +170,31 @@ def delete_workflow_definitions(**kwargs):
|
|||
IMPL.delete_workflow_definitions(**kwargs)
|
||||
|
||||
|
||||
def create_dynamic_action(values):
|
||||
return IMPL.create_dynamic_action(values)
|
||||
# Dynamic actions.
|
||||
|
||||
def get_dynamic_action_definition(identifier, namespace='', fields=()):
|
||||
return IMPL.get_dynamic_action_definition(identifier, fields, namespace)
|
||||
|
||||
|
||||
def delete_dynamic_action(identifier, namespace=''):
|
||||
return IMPL.delete_dynamic_action(identifier, namespace)
|
||||
def load_dynamic_action_definition(identifier, namespace='', fields=()):
|
||||
return IMPL.load_dynamic_action_definition(identifier, fields, namespace)
|
||||
|
||||
|
||||
def update_dynamic_action(identifier, values, namespace=''):
|
||||
return IMPL.update_dynamic_action(identifier, values, namespace)
|
||||
def create_dynamic_action_definition(values):
|
||||
return IMPL.create_dynamic_action_definition(values)
|
||||
|
||||
|
||||
def get_dynamic_action(identifier, namespace='', fields=()):
|
||||
return IMPL.get_dynamic_action(identifier, fields, namespace)
|
||||
def delete_dynamic_action_definition(identifier, namespace=''):
|
||||
return IMPL.delete_dynamic_action_definition(identifier, namespace)
|
||||
|
||||
|
||||
def get_dynamic_actions(limit=None, marker=None, sort_keys=None,
|
||||
sort_dirs=None, fields=None, **kwargs):
|
||||
return IMPL.get_dynamic_actions(
|
||||
def update_dynamic_action_definition(identifier, values, namespace=''):
|
||||
return IMPL.update_dynamic_action_definition(identifier, values, namespace)
|
||||
|
||||
|
||||
def get_dynamic_action_definitions(limit=None, marker=None, sort_keys=None,
|
||||
sort_dirs=None, fields=None, **kwargs):
|
||||
return IMPL.get_dynamic_action_definitions(
|
||||
limit=limit,
|
||||
marker=marker,
|
||||
sort_keys=sort_keys,
|
||||
|
@ -198,10 +204,24 @@ def get_dynamic_actions(limit=None, marker=None, sort_keys=None,
|
|||
)
|
||||
|
||||
|
||||
# Code sources.
|
||||
|
||||
def get_code_source(identifier, namespace='', fields=()):
|
||||
return IMPL.get_code_source(identifier, fields, namespace=namespace)
|
||||
|
||||
|
||||
def create_code_source(values):
|
||||
return IMPL.create_code_source(values)
|
||||
|
||||
|
||||
def update_code_source(identifier, values, namespace=''):
|
||||
return IMPL.update_code_source(identifier, values, namespace=namespace)
|
||||
|
||||
|
||||
def delete_code_source(name, namespace=''):
|
||||
return IMPL.delete_code_source(name, namespace=namespace)
|
||||
|
||||
|
||||
def get_code_sources(limit=None, marker=None, sort_keys=None,
|
||||
sort_dirs=None, fields=None, **kwargs):
|
||||
return IMPL.get_code_sources(
|
||||
|
@ -214,17 +234,7 @@ def get_code_sources(limit=None, marker=None, sort_keys=None,
|
|||
)
|
||||
|
||||
|
||||
def delete_code_source(name, namespace=''):
|
||||
return IMPL.delete_code_source(name, namespace=namespace)
|
||||
|
||||
|
||||
def get_code_source(identifier, namespace='', fields=()):
|
||||
return IMPL.get_code_source(identifier, fields, namespace=namespace)
|
||||
|
||||
|
||||
def create_code_source(values):
|
||||
return IMPL.create_code_source(values)
|
||||
|
||||
# Action definitions.
|
||||
|
||||
def get_action_definition_by_id(id, fields=()):
|
||||
return IMPL.get_action_definition_by_id(id, fields=fields)
|
||||
|
|
|
@ -640,7 +640,7 @@ def delete_workflow_definitions(session=None, **kwargs):
|
|||
return _delete_all(models.WorkflowDefinition, **kwargs)
|
||||
|
||||
|
||||
# Action definitions.
|
||||
# Code sources.
|
||||
|
||||
@b.session_aware()
|
||||
def create_code_source(values, session=None):
|
||||
|
@ -709,9 +709,11 @@ def delete_code_source(identifier, namespace='', session=None):
|
|||
session.delete(code_src)
|
||||
|
||||
|
||||
# Dynamic actions.
|
||||
|
||||
@b.session_aware()
|
||||
def create_dynamic_action(values, session=None):
|
||||
action_def = models.DynamicAction()
|
||||
def create_dynamic_action_definition(values, session=None):
|
||||
action_def = models.DynamicActionDefinition()
|
||||
|
||||
action_def.update(values.copy())
|
||||
|
||||
|
@ -719,7 +721,8 @@ def create_dynamic_action(values, session=None):
|
|||
action_def.save(session=session)
|
||||
except db_exc.DBDuplicateEntry:
|
||||
raise exc.DBDuplicateEntryError(
|
||||
"Duplicate entry for Action[name=%s, namespace=%s, project_id=%s]"
|
||||
"Duplicate entry for dynamic action definition "
|
||||
"[name=%s, namespace=%s, project_id=%s]"
|
||||
% (action_def.name, action_def.namespace, action_def.project_id)
|
||||
)
|
||||
|
||||
|
@ -727,8 +730,9 @@ def create_dynamic_action(values, session=None):
|
|||
|
||||
|
||||
@b.session_aware()
|
||||
def update_dynamic_action(identifier, values, namespace='', session=None):
|
||||
action_def = get_dynamic_action(identifier, namespace=namespace)
|
||||
def update_dynamic_action_definition(identifier, values, namespace='',
|
||||
session=None):
|
||||
action_def = get_dynamic_action_definition(identifier, namespace=namespace)
|
||||
|
||||
action_def.update(values.copy())
|
||||
|
||||
|
@ -736,9 +740,10 @@ def update_dynamic_action(identifier, values, namespace='', session=None):
|
|||
|
||||
|
||||
@b.session_aware()
|
||||
def get_dynamic_action(identifier, fields=(), namespace='', session=None):
|
||||
def get_dynamic_action_definition(identifier, fields=(), namespace='',
|
||||
session=None):
|
||||
action = _get_db_object_by_name_and_namespace_or_id(
|
||||
models.DynamicAction,
|
||||
models.DynamicActionDefinition,
|
||||
identifier,
|
||||
namespace=namespace,
|
||||
columns=fields
|
||||
|
@ -754,21 +759,34 @@ def get_dynamic_action(identifier, fields=(), namespace='', session=None):
|
|||
|
||||
|
||||
@b.session_aware()
|
||||
def get_dynamic_actions(fields=None, session=None, **kwargs):
|
||||
def load_dynamic_action_definition(identifier, fields=(), namespace='',
|
||||
session=None):
|
||||
return _get_db_object_by_name_and_namespace_or_id(
|
||||
models.DynamicActionDefinition,
|
||||
identifier,
|
||||
namespace=namespace,
|
||||
columns=fields
|
||||
)
|
||||
|
||||
|
||||
@b.session_aware()
|
||||
def get_dynamic_action_definitions(fields=None, session=None, **kwargs):
|
||||
return _get_collection(
|
||||
model=models.DynamicAction,
|
||||
model=models.DynamicActionDefinition,
|
||||
fields=fields,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
@b.session_aware()
|
||||
def delete_dynamic_action(identifier, namespace='', session=None):
|
||||
action_def = get_dynamic_action(identifier, namespace)
|
||||
print(action_def)
|
||||
def delete_dynamic_action_definition(identifier, namespace='', session=None):
|
||||
action_def = get_dynamic_action_definition(identifier, namespace)
|
||||
|
||||
session.delete(action_def)
|
||||
|
||||
|
||||
# Action definitions.
|
||||
|
||||
@b.session_aware()
|
||||
def get_action_definition_by_id(id, fields=(), session=None):
|
||||
action_def = _get_db_object_by_id(
|
||||
|
@ -793,6 +811,7 @@ def get_action_definition(identifier, fields=(), session=None, namespace=''):
|
|||
namespace=namespace,
|
||||
columns=fields
|
||||
)
|
||||
|
||||
# If the action was not found in the given namespace,
|
||||
# look in the default namespace
|
||||
if not a_def:
|
||||
|
|
|
@ -200,7 +200,17 @@ class CodeSource(mb.MistralSecureModelBase):
|
|||
"""Contains info about registered CodeSources."""
|
||||
|
||||
__tablename__ = 'code_sources'
|
||||
__table_args__ = (
|
||||
sa.UniqueConstraint(
|
||||
'name',
|
||||
'namespace',
|
||||
'project_id'
|
||||
),
|
||||
sa.Index('%s_project_id' % __tablename__, 'project_id'),
|
||||
sa.Index('%s_scope' % __tablename__, 'scope'),
|
||||
)
|
||||
|
||||
# Main properties.
|
||||
id = mb.id_column()
|
||||
name = sa.Column(sa.String(255))
|
||||
src = sa.Column(sa.Text())
|
||||
|
@ -208,45 +218,36 @@ class CodeSource(mb.MistralSecureModelBase):
|
|||
namespace = sa.Column(sa.String(255), nullable=True)
|
||||
tags = sa.Column(st.JsonListType())
|
||||
|
||||
|
||||
class DynamicActionDefinition(mb.MistralSecureModelBase):
|
||||
"""Contains info about registered Dynamic Actions."""
|
||||
|
||||
__tablename__ = 'dynamic_action_definitions'
|
||||
__table_args__ = (
|
||||
sa.UniqueConstraint(
|
||||
'name',
|
||||
'namespace',
|
||||
'project_id'),
|
||||
|
||||
'project_id'
|
||||
),
|
||||
sa.Index('%s_project_id' % __tablename__, 'project_id'),
|
||||
sa.Index('%s_scope' % __tablename__, 'scope'),
|
||||
)
|
||||
|
||||
|
||||
class DynamicAction(mb.MistralSecureModelBase):
|
||||
"""Contains info about registered Dynamic Actions."""
|
||||
|
||||
__tablename__ = 'dynamic_actions'
|
||||
|
||||
# Main properties.
|
||||
id = mb.id_column()
|
||||
name = sa.Column(sa.String(255))
|
||||
namespace = sa.Column(sa.String(255), nullable=True)
|
||||
class_name = sa.Column(sa.String(255))
|
||||
__table_args__ = (
|
||||
sa.UniqueConstraint(
|
||||
'name',
|
||||
'namespace',
|
||||
'project_id'),
|
||||
sa.Index('%s_project_id' % __tablename__, 'project_id'),
|
||||
sa.Index('%s_scope' % __tablename__, 'scope'),
|
||||
)
|
||||
|
||||
|
||||
DynamicAction.code_source_id = sa.Column(
|
||||
DynamicActionDefinition.code_source_id = sa.Column(
|
||||
sa.String(36),
|
||||
sa.ForeignKey(CodeSource.id, ondelete='CASCADE'),
|
||||
nullable=False
|
||||
)
|
||||
|
||||
# Execution objects.
|
||||
|
||||
# Execution objects.
|
||||
|
||||
class Execution(mb.MistralSecureModelBase):
|
||||
__abstract__ = True
|
||||
|
|
|
@ -72,9 +72,7 @@ def update_code_source(identifier, src_code, namespace=''):
|
|||
return db_api.update_code_source(
|
||||
identifier=identifier,
|
||||
namespace=namespace,
|
||||
values={
|
||||
'src': src_code,
|
||||
}
|
||||
values={'src': src_code}
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -23,10 +23,11 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
def create_dynamic_actions(action_list, namespace=''):
|
||||
created_actions = []
|
||||
|
||||
with db_api.transaction():
|
||||
for action in action_list:
|
||||
created_actions.append(
|
||||
db_api.create_dynamic_action({
|
||||
db_api.create_dynamic_action_definition({
|
||||
'name': action['name'],
|
||||
'class_name': action['class_name'],
|
||||
'namespace': namespace,
|
||||
|
@ -39,7 +40,7 @@ def create_dynamic_actions(action_list, namespace=''):
|
|||
|
||||
def delete_dynamic_action(identifier, namespace=''):
|
||||
with db_api.transaction():
|
||||
return db_api.delete_dynamic_action(
|
||||
return db_api.delete_dynamic_action_definition(
|
||||
identifier,
|
||||
namespace
|
||||
)
|
||||
|
@ -48,7 +49,7 @@ def delete_dynamic_action(identifier, namespace=''):
|
|||
def get_dynamic_actions(limit=None, marker=None, sort_keys=None,
|
||||
sort_dirs=None, fields=None, **kwargs):
|
||||
with db_api.transaction():
|
||||
return db_api.get_dynamic_actions(
|
||||
return db_api.get_dynamic_action_definitions(
|
||||
limit=limit,
|
||||
marker=marker,
|
||||
sort_keys=sort_keys,
|
||||
|
@ -60,7 +61,7 @@ def get_dynamic_actions(limit=None, marker=None, sort_keys=None,
|
|||
|
||||
def get_dynamic_action(identifier, namespace=''):
|
||||
with db_api.transaction():
|
||||
return db_api.get_dynamic_action(
|
||||
return db_api.get_dynamic_action_definition(
|
||||
identifier,
|
||||
namespace=namespace
|
||||
)
|
||||
|
@ -68,7 +69,7 @@ def get_dynamic_action(identifier, namespace=''):
|
|||
|
||||
def update_dynamic_action(identifier, values, namespace=''):
|
||||
with db_api.transaction():
|
||||
return db_api.update_dynamic_action(
|
||||
return db_api.update_dynamic_action_definition(
|
||||
identifier,
|
||||
values,
|
||||
namespace
|
||||
|
|
|
@ -36,7 +36,6 @@ NAMESPACE = "ns"
|
|||
|
||||
|
||||
class DynamicActionProviderTest(base.DbTestCase):
|
||||
|
||||
def _create_code_source(self, namespace=''):
|
||||
return code_sources_service.create_code_source(
|
||||
name='code_source',
|
||||
|
@ -67,7 +66,7 @@ class DynamicActionProviderTest(base.DbTestCase):
|
|||
namespace=namespace
|
||||
)
|
||||
|
||||
def test_Dynamic_actions(self):
|
||||
def test_dynamic_actions(self):
|
||||
provider = dynamic_action.DynamicActionProvider()
|
||||
|
||||
action_descs = provider.find_all()
|
||||
|
@ -75,6 +74,7 @@ class DynamicActionProviderTest(base.DbTestCase):
|
|||
self.assertEqual(0, len(action_descs))
|
||||
|
||||
code_source = self._create_code_source()
|
||||
|
||||
self._create_dynamic_actions(code_source_id=code_source['id'])
|
||||
|
||||
action_descs = provider.find_all()
|
||||
|
@ -91,6 +91,7 @@ class DynamicActionProviderTest(base.DbTestCase):
|
|||
self.assertEqual(0, len(action_descs))
|
||||
|
||||
code_source = self._create_code_source()
|
||||
|
||||
self._create_dynamic_actions(code_source_id=code_source['id'])
|
||||
|
||||
action_descs = provider.find_all()
|
||||
|
@ -103,7 +104,7 @@ class DynamicActionProviderTest(base.DbTestCase):
|
|||
|
||||
self.assertEqual(0, len(action_descs))
|
||||
|
||||
def test_Dynamic_actions_with_namespace(self):
|
||||
def test_dynamic_actions_with_namespace(self):
|
||||
provider = dynamic_action.DynamicActionProvider()
|
||||
|
||||
action_descs = provider.find_all()
|
||||
|
@ -111,6 +112,7 @@ class DynamicActionProviderTest(base.DbTestCase):
|
|||
self.assertEqual(0, len(action_descs))
|
||||
|
||||
code_source = self._create_code_source()
|
||||
|
||||
self._create_dynamic_actions(
|
||||
code_source_id=code_source['id'],
|
||||
namespace=NAMESPACE
|
||||
|
|
|
@ -24,7 +24,6 @@ NAMESPACE = "NS"
|
|||
|
||||
|
||||
class TestCodeSourcesController(base.APITest):
|
||||
|
||||
def _create_code_source(self, module_name, file_content,
|
||||
namespace=NAMESPACE, expect_errors=False):
|
||||
return self.app.post(
|
||||
|
@ -46,7 +45,8 @@ class TestCodeSourcesController(base.APITest):
|
|||
|
||||
resp = self._create_code_source(
|
||||
mod_name,
|
||||
FILE_CONTENT)
|
||||
FILE_CONTENT
|
||||
)
|
||||
|
||||
resp_json = resp.json
|
||||
|
||||
|
|
|
@ -12,21 +12,23 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from mistral.services import actions
|
||||
from mistral.tests.unit.api import base
|
||||
|
||||
FILE_CONTENT = """from mistral_lib import actions
|
||||
|
||||
TEST_MODULE_TEXT = """
|
||||
from mistral_lib import actions
|
||||
|
||||
class DummyAction(actions.Action):
|
||||
def run(self, context):
|
||||
return None
|
||||
return "Hello from the dummy action 1!"
|
||||
|
||||
def test(self, context):
|
||||
return None
|
||||
|
||||
class DummyAction2(actions.Action):
|
||||
def run(self, context):
|
||||
return None
|
||||
return "Hello from the dummy action 2!"
|
||||
|
||||
def test(self, context):
|
||||
return None"""
|
||||
|
@ -46,19 +48,20 @@ dummy_action:
|
|||
|
||||
|
||||
class TestDynamicActionsController(base.APITest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDynamicActionsController, self).setUp()
|
||||
|
||||
resp = self._create_code_source().json
|
||||
|
||||
self.code_source_id = resp.get('code_sources')[0].get('id')
|
||||
|
||||
self.addCleanup(self._delete_code_source)
|
||||
|
||||
def _create_code_source(self):
|
||||
return self.app.post(
|
||||
'/v2/code_sources',
|
||||
upload_files=[
|
||||
('modulename', 'filename', FILE_CONTENT.encode())
|
||||
('test_dummy_module', 'filename', TEST_MODULE_TEXT.encode())
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -70,15 +73,14 @@ class TestDynamicActionsController(base.APITest):
|
|||
)
|
||||
|
||||
def _delete_code_source(self):
|
||||
return self.app.delete(
|
||||
'/v2/code_sources/modulename',
|
||||
)
|
||||
return self.app.delete('/v2/code_sources/test_dummy_module')
|
||||
|
||||
def test_create_dynamic_action(self):
|
||||
resp = self._create_dynamic_action(
|
||||
CREATE_REQUEST.format(self.code_source_id)
|
||||
)
|
||||
|
||||
# Check the structure of the response.
|
||||
resp_json = resp.json
|
||||
|
||||
self.assertEqual(200, resp.status_int)
|
||||
|
@ -95,6 +97,22 @@ class TestDynamicActionsController(base.APITest):
|
|||
self.code_source_id,
|
||||
dynamic_action.get('code_source_id')
|
||||
)
|
||||
|
||||
# Make sure the action can be found via the system action provider
|
||||
# and it's fully functioning.
|
||||
provider = actions.get_system_action_provider()
|
||||
|
||||
action_desc = provider.find('dummy_action')
|
||||
|
||||
self.assertIsNotNone(action_desc)
|
||||
|
||||
action = action_desc.instantiate({}, None)
|
||||
|
||||
self.assertIsNotNone(action)
|
||||
|
||||
self.assertEqual("Hello from the dummy action 1!", action.run(None))
|
||||
|
||||
# Delete the action
|
||||
self.app.delete('/v2/dynamic_actions/dummy_action')
|
||||
|
||||
def test_update_dynamic_action(self):
|
||||
|
@ -158,6 +176,7 @@ class TestDynamicActionsController(base.APITest):
|
|||
CREATE_REQUEST.format(self.code_source_id)
|
||||
)
|
||||
|
||||
# Check the structure of the response
|
||||
self.assertEqual(200, resp.status_int)
|
||||
|
||||
resp = self.app.get('/v2/dynamic_actions/dummy_action')
|
||||
|
@ -172,3 +191,9 @@ class TestDynamicActionsController(base.APITest):
|
|||
)
|
||||
|
||||
self.assertEqual(404, resp.status_int)
|
||||
|
||||
# Make sure the system action provider doesn't find an action
|
||||
# descriptor for the action.
|
||||
provider = actions.get_system_action_provider()
|
||||
|
||||
self.assertIsNone(provider.find('dummy_action'))
|
||||
|
|
Loading…
Reference in New Issue