Merge "Refactor get_task_spec using mechanism of polymorphic DSL entities"

This commit is contained in:
Jenkins 2015-09-15 11:04:56 +00:00 committed by Gerrit Code Review
commit 529638fde6
8 changed files with 44 additions and 93 deletions

View File

@ -356,8 +356,8 @@ class ExecutionTestsV2(base.TestCase):
self.client.delete_obj('workflows', wf)
self.client.workflows = []
for wf in self.client.executions:
self.client.delete_obj('executions', wf)
for ex in self.client.executions:
self.client.delete_obj('executions', ex)
self.client.executions = []
super(ExecutionTestsV2, self).tearDown()

View File

@ -146,8 +146,7 @@ class TestWorkbooksController(base.FunctionalTest):
)
self.assertEqual(resp.status_int, 400)
self.assertIn("Task properties 'action' and 'workflow' "
"can't be specified both", resp.body)
self.assertIn("Invalid DSL", resp.body)
@mock.patch.object(workbooks, "create_workbook_v2", MOCK_WORKBOOK)
def test_post(self):
@ -180,8 +179,7 @@ class TestWorkbooksController(base.FunctionalTest):
)
self.assertEqual(resp.status_int, 400)
self.assertIn("Task properties 'action' and 'workflow' "
"can't be specified both", resp.body)
self.assertIn("Invalid DSL", resp.body)
@mock.patch.object(db_api, "delete_workbook", MOCK_DELETE)
def test_delete(self):
@ -232,8 +230,7 @@ class TestWorkbooksController(base.FunctionalTest):
self.assertEqual(resp.status_int, 200)
self.assertFalse(resp.json['valid'])
self.assertIn("Task properties 'action' and 'workflow' "
"can't be specified both", resp.json['error'])
self.assertIn("Invalid DSL", resp.json['error'])
def test_validate_dsl_parse_exception(self):
resp = self.app.post(

View File

@ -221,8 +221,7 @@ class TestWorkflowsController(base.FunctionalTest):
)
self.assertEqual(resp.status_int, 400)
self.assertIn("Task properties 'action' and 'workflow' "
"can't be specified both", resp.body)
self.assertIn("Invalid DSL", resp.body)
@mock.patch.object(db_api, "create_workflow_definition")
def test_post(self, mock_mtd):
@ -264,8 +263,7 @@ class TestWorkflowsController(base.FunctionalTest):
)
self.assertEqual(resp.status_int, 400)
self.assertIn("Task properties 'action' and 'workflow' "
"can't be specified both", resp.body)
self.assertIn("Invalid DSL", resp.body)
@mock.patch.object(db_api, "delete_workflow_definition", MOCK_DELETE)
def test_delete(self):
@ -416,8 +414,7 @@ class TestWorkflowsController(base.FunctionalTest):
self.assertEqual(resp.status_int, 200)
self.assertFalse(resp.json['valid'])
self.assertIn("Task properties 'action' and 'workflow' "
"can't be specified both", resp.json['error'])
self.assertIn("Invalid DSL", resp.json['error'])
def test_validate_dsl_parse_exception(self):
resp = self.app.post(

View File

@ -15,33 +15,14 @@
from oslo_log import log as logging
from mistral import exceptions
from mistral.tests import base
from mistral.tests.unit.workbook.v2 import base as v2_base
from mistral import utils
from mistral.workbook.v2 import tasks
from mistral.workbook.v2 import workflows
LOG = logging.getLogger(__name__)
class TaskSpecListTest(base.BaseTest):
def test_get_class(self):
spec_list_cls = tasks.TaskSpecList.get_class('direct')
self.assertIs(spec_list_cls, tasks.DirectWfTaskSpecList)
def test_get_class_notfound(self):
exc = self.assertRaises(
exceptions.NotFoundException,
tasks.TaskSpecList.get_class,
"invalid"
)
self.assertIn("Can not find task list specification", str(exc))
class TaskSpecValidation(v2_base.WorkflowSpecValidationTestCase):
def test_type_injection(self):
tests = [

View File

@ -20,7 +20,6 @@ import yaml
from mistral import exceptions as exc
from mistral.tests.unit.workbook.v2 import base
from mistral import utils
from mistral.workbook.v2 import tasks
LOG = logging.getLogger(__name__)
@ -56,8 +55,6 @@ class WorkflowSpecValidation(base.WorkflowSpecValidationTestCase):
self.assertEqual(1, len(wfs_spec.get_workflows()))
self.assertEqual('test', wfs_spec.get_workflows()[0].get_name())
self.assertEqual('direct', wfs_spec.get_workflows()[0].get_type())
self.assertIsInstance(wfs_spec.get_workflows()[0].get_tasks(),
tasks.DirectWfTaskSpecList)
def test_direct_workflow_invalid_task(self):
overlay = {
@ -102,8 +99,6 @@ class WorkflowSpecValidation(base.WorkflowSpecValidationTestCase):
self.assertEqual(1, len(wfs_spec.get_workflows()))
self.assertEqual('test', wfs_spec.get_workflows()[0].get_name())
self.assertEqual('reverse', wfs_spec.get_workflows()[0].get_type())
self.assertIsInstance(wfs_spec.get_workflows()[0].get_tasks(),
tasks.ReverseWfTaskSpecList)
def test_reverse_workflow_invalid_task(self):
overlay = {'test': {'type': 'reverse', 'tasks': {}}}

View File

@ -115,13 +115,6 @@ def get_workflow_list_spec_from_yaml(text):
def get_task_spec(spec_dict):
if _get_spec_version(spec_dict) == V2_0:
workflow_type = spec_dict.get('type')
if workflow_type == 'direct':
return tasks_v2.DirectWorkflowTaskSpec(spec_dict)
elif workflow_type == 'reverse':
return tasks_v2.ReverseWorkflowTaskSpec(spec_dict)
else:
raise Exception('Unsupported workflow type "%s".' % workflow_type)
return base.instantiate_spec(tasks_v2.TaskSpec, spec_dict)
return None

View File

@ -33,7 +33,7 @@ WITH_ITEMS_PTRN = re.compile(
class TaskSpec(base.BaseSpec):
# See http://json-schema.org
_type = None
_polymorphic_key = ('type', 'direct')
_schema = {
"type": "object",
@ -58,7 +58,27 @@ class TaskSpec(base.BaseSpec):
"target": types.NONEMPTY_STRING,
"keep-result": types.YAQL_OR_BOOLEAN
},
"additionalProperties": False
"additionalProperties": False,
"anyOf": [
{
"not": {
"type": "object",
"required": ["action", "workflow"]
},
},
{
"oneOf": [
{
"type": "object",
"required": ["action"]
},
{
"type": "object",
"required": ["workflow"]
}
]
}
]
}
def __init__(self, data):
@ -83,22 +103,16 @@ class TaskSpec(base.BaseSpec):
self._target = data.get('target')
self._keep_result = data.get('keep-result', True)
self._inject_type()
self._process_action_and_workflow()
def validate_schema(self):
super(TaskSpec, self).validate_schema()
self._transform_with_items()
action = self._data.get('action')
workflow = self._data.get('workflow')
if action and workflow:
msg = ("Task properties 'action' and 'workflow' can't be"
" specified both: %s" % self._data)
raise exc.InvalidModelException(msg)
self._transform_with_items()
# Validate YAQL expressions.
if action or workflow:
inline_params = self._parse_cmd_and_input(action or workflow)[1]
@ -146,10 +160,6 @@ class TaskSpec(base.BaseSpec):
return with_items
def _inject_type(self):
if self._type:
self._data['type'] = self._type
def _process_action_and_workflow(self):
params = {}
@ -198,7 +208,7 @@ class TaskSpec(base.BaseSpec):
class DirectWorkflowTaskSpec(TaskSpec):
_type = 'direct'
_polymorphic_value = 'direct'
_on_clause_type = {
"oneOf": [
@ -210,7 +220,7 @@ class DirectWorkflowTaskSpec(TaskSpec):
_direct_workflow_schema = {
"type": "object",
"properties": {
"type": {"enum": [_type]},
"type": {"enum": [_polymorphic_value]},
"join": {
"oneOf": [
{"enum": ["all", "one"]},
@ -270,12 +280,12 @@ class DirectWorkflowTaskSpec(TaskSpec):
class ReverseWorkflowTaskSpec(TaskSpec):
_type = 'reverse'
_polymorphic_value = 'reverse'
_reverse_workflow_schema = {
"type": "object",
"properties": {
"type": {"enum": [_type]},
"type": {"enum": [_polymorphic_value]},
"requires": {
"oneOf": [types.NONEMPTY_STRING, types.UNIQUE_STRING_LIST]
}
@ -299,28 +309,3 @@ class ReverseWorkflowTaskSpec(TaskSpec):
class TaskSpecList(base.BaseSpecList):
item_class = TaskSpec
@staticmethod
def get_class(wf_type):
"""Gets a task specification list class by given workflow type.
:param wf_type: Workflow type
:returns: Task specification list class
"""
for spec_list_cls in utils.iter_subclasses(TaskSpecList):
if wf_type == spec_list_cls.__type__:
return spec_list_cls
msg = ("Can not find task list specification with workflow type:"
" %s" % wf_type)
raise exc.NotFoundException(msg)
class DirectWfTaskSpecList(TaskSpecList):
__type__ = 'direct'
item_class = DirectWorkflowTaskSpec
class ReverseWfTaskSpecList(TaskSpecList):
__type__ = 'reverse'
item_class = ReverseWorkflowTaskSpec

View File

@ -57,10 +57,13 @@ class WorkflowSpec(base.BaseSpec):
'task-defaults',
task_defaults.TaskDefaultsSpec
)
self._tasks = self._spec_property(
'tasks',
tasks.TaskSpecList.get_class(self._type)
)
# Inject 'type' here, so instantiate_spec function can recognize the
# specific subclass of TaskSpec.
for task in self._data.get('tasks').itervalues():
task['type'] = self._type
self._tasks = self._spec_property('tasks', tasks.TaskSpecList)
def validate_schema(self):
super(WorkflowSpec, self).validate_schema()