Add metadata to the result of action serialization

In addition to action name, now title, description and help text
also appear in the serialized action, if corresponding metaclasses
are attached to the method. If no title specified by metadata, it
is equal to method name in serialization result.

Tests are updated to check new functionality. All test cases for
actions serialization are now gathered in one place.

Change-Id: I00c46677ed6bef3c8fcf93f07a1abc004c7304d3
Partially-implements: blueprint static-actions
This commit is contained in:
Valerii Kovalchuk
2016-06-16 13:06:35 +03:00
parent 91fd92c136
commit 9bdf4c0831
6 changed files with 70 additions and 95 deletions

View File

@@ -225,7 +225,7 @@ class TaskExecutor(object):
self.session.finish()
try:
action_result = serializer.serialize(action_result)
action_result = serializer.serialize(action_result, executor)
except Exception as e:
return self.exception_result(e, None, '<result>')

View File

@@ -25,17 +25,18 @@ class ObjRef(object):
self.ref_obj = obj
def serialize(obj):
return serialize_model(obj, None, True)[0]['Objects']
def serialize(obj, executor):
return serialize_model(obj, executor, True)[0]['Objects']
def _serialize_object(root_object, designer_attributes, allow_refs):
def _serialize_object(root_object, designer_attributes, allow_refs,
executor):
serialized_objects = set()
obj = root_object
while True:
obj, need_another_pass = _pass12_serialize(
obj, None, serialized_objects, designer_attributes)
obj, None, serialized_objects, designer_attributes, executor)
if not need_another_pass:
break
tree = [obj]
@@ -44,10 +45,7 @@ def _serialize_object(root_object, designer_attributes, allow_refs):
def serialize_model(root_object, executor, allow_refs=False):
if executor is not None:
designer_attributes = executor.object_store.designer_attributes
else:
designer_attributes = None
designer_attributes = executor.object_store.designer_attributes
if root_object is None:
tree = None
@@ -56,12 +54,10 @@ def serialize_model(root_object, executor, allow_refs=False):
serialized_objects = set()
else:
tree, serialized_objects = _serialize_object(
root_object, designer_attributes, allow_refs)
tree_copy, _ = _serialize_object(root_object, None, allow_refs)
if executor is not None:
attributes = executor.attribute_store.serialize(serialized_objects)
else:
attributes = []
root_object, designer_attributes, allow_refs, executor)
tree_copy, _ = _serialize_object(root_object, None, allow_refs,
executor)
attributes = executor.attribute_store.serialize(serialized_objects)
return {
'Objects': tree,
@@ -70,19 +66,33 @@ def serialize_model(root_object, executor, allow_refs=False):
}, serialized_objects
def _serialize_available_action(obj, current_actions):
def _serialize_available_action(obj, current_actions, executor):
result = {}
actions = obj.type.find_methods(lambda m: m.is_action)
for action in actions:
action_id = '{0}_{1}'.format(obj.object_id, action.name)
entry = current_actions.get(action_id, {'enabled': True})
entry['name'] = action.name
context = executor.create_type_context(action.declaring_type)
meta = action.get_meta(context)
meta_dict = {item.type.name: item for item in meta}
title = meta_dict.get('io.murano.metadata.Title')
if title:
entry['title'] = title.get_property('text')
else:
entry['title'] = action.name
description = meta_dict.get('io.murano.metadata.Description')
if description:
entry['description'] = description.get_property('text')
help_text = meta_dict.get('io.murano.metadata.HelpText')
if help_text:
entry['helpText'] = help_text.get_property('text')
result[action_id] = entry
return result
def _pass12_serialize(value, parent, serialized_objects,
designer_attributes_getter):
designer_attributes_getter, executor):
if isinstance(value, dsl.MuranoObjectInterface):
value = value.object
if isinstance(value, (six.string_types,
@@ -103,10 +113,11 @@ def _pass12_serialize(value, parent, serialized_objects,
result['?'].update(designer_attributes_getter(value.object_id))
# deserialize and merge list of actions
result['?']['_actions'] = _serialize_available_action(
value, result['?'].get('_actions', {}))
value, result['?'].get('_actions', {}), executor)
serialized_objects.add(value.object_id)
return _pass12_serialize(
result, value, serialized_objects, designer_attributes_getter)
result, value, serialized_objects, designer_attributes_getter,
executor)
elif isinstance(value, utils.MappingType):
result = {}
need_another_pass = False
@@ -115,7 +126,7 @@ def _pass12_serialize(value, parent, serialized_objects,
result_key = str(d_key)
result_value = _pass12_serialize(
d_value, parent, serialized_objects,
designer_attributes_getter)
designer_attributes_getter, executor)
result[result_key] = result_value[0]
if result_value[1]:
need_another_pass = True
@@ -125,7 +136,8 @@ def _pass12_serialize(value, parent, serialized_objects,
result = []
for t in value:
v, nmp = _pass12_serialize(
t, parent, serialized_objects, designer_attributes_getter)
t, parent, serialized_objects, designer_attributes_getter,
executor)
if nmp:
need_another_pass = True
result.append(v)

View File

@@ -1,5 +1,7 @@
Name: ContractExamples
Extends: CommonParent
Properties:
sampleClass:
Contract: $.class(SampleClass1)
@@ -146,3 +148,18 @@ Methods:
Body:
Return: $arg
testActionMeta:
Scope: Public
Meta:
- io.murano.metadata.Title:
text: "Title of the method"
- io.murano.metadata.Description:
text: "Description of the method"
- io.murano.metadata.HelpText:
text: "HelpText of the method"
notAction:
Scope: Session
testAction:
Scope: Public

View File

@@ -32,7 +32,8 @@ class TestConstruction(test_case.DslTestCase):
self.traces)
def test_new_with_ownership(self):
obj = serializer.serialize(self._runner.testNewWithOwnership())
obj = serializer.serialize(self._runner.testNewWithOwnership(),
self._runner.executor)
self.assertEqual('STRING', obj.get('property1'))
self.assertIsNotNone('string', obj.get('xxx'))
self.assertEqual('STR', obj['xxx'].get('property1'))

View File

@@ -89,14 +89,28 @@ class TestResultsSerializer(test_case.DslTestCase):
"""
serialized = self._runner.serialized_model
self.assertIsInstance(
serialized['Objects']['?'].get('_actions'), dict)
for action in serialized['Objects']['?']['_actions'].values():
actions = serialized['Objects']['?'].get('_actions')
self.assertIsInstance(actions, dict)
action_names = [action['name'] for action in actions.values()]
self.assertIn('testAction', action_names)
self.assertNotIn('notAction', action_names)
self.assertIn('testRootMethod', action_names)
action_meta = None
for action in actions.values():
self.assertIsInstance(action.get('enabled'), bool)
self.assertIsInstance(action.get('name'), six.string_types)
self.assertThat(
action['name'],
matchers.StartsWith('test'))
if action['name'] == 'testActionMeta':
action_meta = action
else:
self.assertEqual(action['title'], action['name'])
self.assertIsNotNone(action_meta)
self.assertEqual(action_meta['title'], "Title of the method")
self.assertEqual(action_meta['description'],
"Description of the method")
self.assertEqual(action_meta['helpText'], "HelpText of the method")
def test_attribute_serialization(self):
"""Test that attributes produced by MuranoPL code are persisted
@@ -152,4 +166,4 @@ class TestResultsSerializer(test_case.DslTestCase):
'key5': {'x': 'y'},
'key6': [{'w': 'q'}]
},
serializer.serialize(result))
serializer.serialize(result, runner.executor))

View File

@@ -12,79 +12,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import mock
from murano.dsl import dsl_types
from murano.dsl import serializer
from murano.services import actions
from murano.tests.unit import base
class TestActionsSerializer(base.MuranoTestCase):
def setUp(self):
super(TestActionsSerializer, self).setUp()
def _get_mocked_obj(self):
method1 = mock.Mock()
method1.scope = dsl_types.MethodScopes.Public
method1.name = 'method1'
method2 = mock.Mock()
method2.usage = dsl_types.MethodUsages.Runtime
method2.name = 'method2'
method3 = mock.Mock()
method3.scope = dsl_types.MethodScopes.Public
method3.name = 'method3'
method4 = mock.Mock()
method4.usage = dsl_types.MethodUsages.Action
method4.name = 'method4'
for method in [method1, method2, method3, method4]:
method.is_action = (method.scope ==
dsl_types.MethodScopes.Public or
method.usage == dsl_types.MethodUsages.Action)
obj2_type = mock.Mock()
obj2_type.declared_parents = []
obj2_type.methods = {'method3': method3}
obj2_type.type.find_methods = lambda p: filter(p, [method3])
obj = mock.Mock()
obj.object_id = 'id1'
obj.type.declared_parents = [obj2_type]
obj.type.methods = {'method1': method1, 'method2': method2,
'method4': method4}
obj.type.find_methods = lambda p: filter(
p, [method1, method2, method3, method4])
return obj
def test_object_actions_serialization(self):
obj = self._get_mocked_obj()
obj_actions = serializer._serialize_available_action(obj, {})
self.assertIn('id1_method1', obj_actions)
self.assertIn('id1_method4', obj_actions)
expected_result = {'name': 'method1', 'enabled': True}
self.assertEqual(expected_result, obj_actions['id1_method1'])
expected_result = {'name': 'method4', 'enabled': True}
self.assertEqual(expected_result, obj_actions['id1_method4'])
def test_that_only_actions_are_serialized(self):
obj = self._get_mocked_obj()
obj_actions = serializer._serialize_available_action(obj, {})
self.assertNotIn('id1_method2', obj_actions)
def test_parent_actions_are_serialized(self):
obj = self._get_mocked_obj()
obj_actions = serializer._serialize_available_action(obj, {})
expected_result = {'name': 'method3', 'enabled': True}
self.assertIn('id1_method3', obj_actions)
self.assertEqual(expected_result, obj_actions['id1_method3'])
class TestActionFinder(base.MuranoTestCase):
def setUp(self):
super(TestActionFinder, self).setUp()