Repository: incubator-ariatosca
Updated Branches:
  refs/heads/ARIA-313-fix-handling-the-required-field-of-inputs ee55bf4ca -> 
51e4ed004 (forced update)


ARIA-313 Fix handling the `required` field of inputs

The required field is handled in the following way:

topology_template inputs:
-------------------------
Every input that is declared as required must be supplied a value while
creating the service.
In addition, supplying inputs that were not declared in the topology
template is forbidden.

workflow inputs:*
----------------
Every input that is declared as required must be supplied a value while
creating/starting the execution.
In addition, supplying inputs that were not declared in the policy_type
is forbidden.
* workflow inputs are defined as properties of policy_types that are
derived from aria.Workflow

operation and interface inputs:
-------------------------------
The validation of the required field of inputs that belong to
operations and interfaces is done only in the parsing stage.
This reasoning follows the TOSCA spirit, where anything that is declared
as required in the type, must be assigned in the corresponding template

I split the logic of merging provided and declared input values into
three steps:

1. Validate that no undeclared inputs were provided.
2. Validate that all required inputs were provided with a value.
3. The actual merging process, which includes type checking.


Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/51e4ed00
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/51e4ed00
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/51e4ed00

Branch: refs/heads/ARIA-313-fix-handling-the-required-field-of-inputs
Commit: 51e4ed0041c3221723eee27a81ebbc49156048b6
Parents: c2b8e65
Author: Avia Efrat <a...@gigaspaces.com>
Authored: Wed Jul 26 15:11:21 2017 +0300
Committer: Avia Efrat <a...@gigaspaces.com>
Committed: Sun Jul 30 12:13:12 2017 +0300

----------------------------------------------------------------------
 aria/modeling/exceptions.py                     |   4 +-
 aria/modeling/mixins.py                         |   2 -
 aria/modeling/service_common.py                 |  13 +++
 aria/modeling/service_instance.py               |   2 +-
 aria/modeling/service_template.py               |   5 +
 aria/modeling/utils.py                          |  79 ++++++-------
 aria/orchestrator/workflow_runner.py            |   4 +
 aria/parser/consumption/style.py                |   4 +
 aria/parser/presentation/presentation.py        |   5 +-
 .../simple_v1_0/modeling/__init__.py            | 114 +++++++++++--------
 .../simple_v1_0/modeling/data_types.py          |   5 +-
 .../simple_v1_0/modeling/interfaces.py          |  12 +-
 .../simple_v1_0/modeling/parameters.py          |   3 +-
 tests/orchestrator/test_workflow_runner.py      |  18 +--
 14 files changed, 159 insertions(+), 111 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51e4ed00/aria/modeling/exceptions.py
----------------------------------------------------------------------
diff --git a/aria/modeling/exceptions.py b/aria/modeling/exceptions.py
index 573efaf..cddc049 100644
--- a/aria/modeling/exceptions.py
+++ b/aria/modeling/exceptions.py
@@ -45,7 +45,7 @@ class CannotEvaluateFunctionException(ModelingException):
     """
 
 
-class MissingRequiredParametersException(ParameterException):
+class MissingRequiredInputsException(ParameterException):
     """
     ARIA modeling exception: Required parameters have been omitted.
     """
@@ -57,7 +57,7 @@ class ParametersOfWrongTypeException(ParameterException):
     """
 
 
-class UndeclaredParametersException(ParameterException):
+class UndeclaredInputsException(ParameterException):
     """
     ARIA modeling exception: Undeclared parameters have been provided.
     """

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51e4ed00/aria/modeling/mixins.py
----------------------------------------------------------------------
diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py
index 883ff4a..4acbe6e 100644
--- a/aria/modeling/mixins.py
+++ b/aria/modeling/mixins.py
@@ -161,8 +161,6 @@ class ParameterMixin(TemplateModelMixin, 
caching.HasCachedMethods):
     :func:`~aria.modeling.functions.evaluate`.
     """
 
-    __tablename__ = 'parameter'
-
     type_name = Column(Text, doc="""
     Type name.
     

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51e4ed00/aria/modeling/service_common.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py
index b533a88..4ce9dae 100644
--- a/aria/modeling/service_common.py
+++ b/aria/modeling/service_common.py
@@ -22,6 +22,7 @@ ARIA modeling service common module
 from sqlalchemy import (
     Column,
     Text,
+    Boolean
 )
 from sqlalchemy.ext.declarative import declared_attr
 
@@ -84,6 +85,18 @@ class InputBase(ParameterMixin):
 
     __tablename__ = 'input'
 
+    required = Column(Boolean, doc="""
+    Is the input mandatory.
+
+    :type: :obj:`bool`
+    """)
+
+    @classmethod
+    def wrap(cls, name, value, description=None, required=True):  # pylint: 
disable=arguments-differ
+        input = super(InputBase, cls).wrap(name, value, description)
+        input.required = required
+        return input
+
     # region many_to_one relationships
 
     @declared_attr

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51e4ed00/aria/modeling/service_instance.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_instance.py 
b/aria/modeling/service_instance.py
index 889465c..ae6a0a0 100644
--- a/aria/modeling/service_instance.py
+++ b/aria/modeling/service_instance.py
@@ -1939,7 +1939,7 @@ class OperationBase(InstanceModelMixin):
     """)
 
     retry_interval = Column(Integer, doc="""
-    Interval between task retry attemps (in seconds).
+    Interval between task retry attempts (in seconds).
 
     :type: :obj:`float`
     """)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51e4ed00/aria/modeling/service_template.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_template.py 
b/aria/modeling/service_template.py
index 22912e2..2246340 100644
--- a/aria/modeling/service_template.py
+++ b/aria/modeling/service_template.py
@@ -343,6 +343,11 @@ class ServiceTemplateBase(TemplateModelMixin):
         context = ConsumptionContext.get_thread_local()
         context.modeling.instance = service
 
+        utils.validate_no_undeclared_inputs(declared_inputs=self.inputs,
+                                            supplied_inputs=inputs or {})
+        
utils.validate_required_inputs_are_supplied(declared_inputs=self.inputs,
+                                                    supplied_inputs=inputs or 
{})
+
         service.inputs = utils.merge_parameter_values(inputs, self.inputs, 
model_cls=models.Input)
         # TODO: now that we have inputs, we should scan properties and inputs 
and evaluate functions
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51e4ed00/aria/modeling/utils.py
----------------------------------------------------------------------
diff --git a/aria/modeling/utils.py b/aria/modeling/utils.py
index e0fd11b..274eb88 100644
--- a/aria/modeling/utils.py
+++ b/aria/modeling/utils.py
@@ -64,81 +64,84 @@ class NodeTemplateContainerHolder(object):
         return self.container.service_template
 
 
-def merge_parameter_values(parameter_values, declared_parameters, model_cls):
+def validate_no_undeclared_inputs(declared_inputs, supplied_inputs):
+
+    undeclared_inputs = [input for input in supplied_inputs if input not in 
declared_inputs]
+    if undeclared_inputs:
+        raise exceptions.UndeclaredInputsException(
+            'Undeclared inputs have been provided: {0}; Declared inputs: {1}'
+            .format(string_list_as_string(undeclared_inputs),
+                    string_list_as_string(declared_inputs.keys())))
+
+
+def validate_required_inputs_are_supplied(declared_inputs, supplied_inputs):
+    required_inputs = [input for input in declared_inputs.values() if 
input.required]
+    missing_required_inputs = [input for input in required_inputs
+                               if input.name not in supplied_inputs and not 
input.value]
+    if missing_required_inputs:
+        raise exceptions.MissingRequiredInputsException(
+            'Required inputs {0} have not been provided values'
+            .format(string_list_as_string(missing_required_inputs)))
+
+
+def merge_parameter_values(provided_values, declared_parameters, model_cls):
     """
     Merges parameter values according to those declared by a type.
 
     Exceptions will be raised for validation errors.
 
-    :param parameter_values: provided parameter values or None
-    :type parameter_values: {:obj:`basestring`: object}
+    :param provided_values: provided parameter values or None
+    :type provided_values: {:obj:`basestring`: object}
     :param declared_parameters: declared parameters
     :type declared_parameters: {:obj:`basestring`: 
:class:`~aria.modeling.models.Parameter`}
+    :param model_cls: the model class that should be created from a provided 
value
+    :type model_cls: :class:`~aria.modeling.models.Input` or 
:class:`~aria.modeling.models.Argument`
     :return: the merged parameters
     :rtype: {:obj:`basestring`: :class:`~aria.modeling.models.Parameter`}
-    :raises ~aria.modeling.exceptions.UndeclaredParametersException: if a key 
in
+    :raises ~aria.modeling.exceptions.UndeclaredInputsException: if a key in
      ``parameter_values`` does not exist in ``declared_parameters``
-    :raises ~aria.modeling.exceptions.MissingRequiredParametersException: if a 
key in
+    :raises ~aria.modeling.exceptions.MissingRequiredInputsException: if a key 
in
      ``declared_parameters`` does not exist in ``parameter_values`` and also 
has no default value
     :raises ~aria.modeling.exceptions.ParametersOfWrongTypeException: if a 
value in
       ``parameter_values`` does not match its type in ``declared_parameters``
     """
 
-    parameter_values = parameter_values or {}
-
-    undeclared_names = 
list(set(parameter_values.keys()).difference(declared_parameters.keys()))
-    if undeclared_names:
-        raise exceptions.UndeclaredParametersException(
-            'Undeclared parameters have been provided: {0}; Declared: {1}'
-            .format(string_list_as_string(undeclared_names),
-                    string_list_as_string(declared_parameters.keys())))
+    provided_values = provided_values or {}
+    provided_values_of_wrong_type = OrderedDict()
+    model_parameters = OrderedDict()
 
-    parameters = OrderedDict()
-
-    missing_names = []
-    wrong_type_values = OrderedDict()
     for declared_parameter_name, declared_parameter in 
declared_parameters.iteritems():
-        if declared_parameter_name in parameter_values:
-            # Value has been provided
-            value = parameter_values[declared_parameter_name]
+        if declared_parameter_name in provided_values:
+            # a value has been provided
+            value = provided_values[declared_parameter_name]
 
             # Validate type
             type_name = declared_parameter.type_name
             try:
                 validate_value_type(value, type_name)
             except ValueError:
-                wrong_type_values[declared_parameter_name] = type_name
+                provided_values_of_wrong_type[declared_parameter_name] = 
type_name
             except RuntimeError:
-                # TODO: This error shouldn't be raised (or caught), but right 
now we lack support
+                # TODO This error shouldn't be raised (or caught), but right 
now we lack support
                 # for custom data_types, which will raise this error. Skipping 
their validation.
                 pass
-
-            # Wrap in Parameter model
-            parameters[declared_parameter_name] = model_cls( # pylint: 
disable=unexpected-keyword-arg
+            model_parameters[declared_parameter_name] = model_cls( # pylint: 
disable=unexpected-keyword-arg
                 name=declared_parameter_name,
                 type_name=type_name,
                 description=declared_parameter.description,
                 value=value)
-        elif declared_parameter.value is not None:
-            # Copy default value from declaration
-            parameters[declared_parameter_name] = 
declared_parameter.instantiate(None)
         else:
-            # Required value has not been provided
-            missing_names.append(declared_parameter_name)
-
-    if missing_names:
-        raise exceptions.MissingRequiredParametersException(
-            'Declared parameters {0} have not been provided values'
-            .format(string_list_as_string(missing_names)))
+            # Copy default value from declaration
+            model_parameters[declared_parameter_name] = 
declared_parameter.instantiate(None)
 
-    if wrong_type_values:
+    if provided_values_of_wrong_type:
         error_message = StringIO()
-        for param_name, param_type in wrong_type_values.iteritems():
+        for param_name, param_type in 
provided_values_of_wrong_type.iteritems():
             error_message.write('Parameter "{0}" is not of declared type 
"{1}"{2}'
                                 .format(param_name, param_type, os.linesep))
         raise 
exceptions.ParametersOfWrongTypeException(error_message.getvalue())
 
-    return parameters
+    return model_parameters
 
 
 def coerce_dict_values(the_dict, report_issues=False):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51e4ed00/aria/orchestrator/workflow_runner.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflow_runner.py 
b/aria/orchestrator/workflow_runner.py
index a85e7d3..0eac61c 100644
--- a/aria/orchestrator/workflow_runner.py
+++ b/aria/orchestrator/workflow_runner.py
@@ -137,6 +137,10 @@ class WorkflowRunner(object):
         else:
             workflow_inputs = 
self.service.workflows[self._workflow_name].inputs
 
+        
modeling_utils.validate_no_undeclared_inputs(declared_inputs=workflow_inputs,
+                                                     supplied_inputs=inputs or 
{})
+        
modeling_utils.validate_required_inputs_are_supplied(declared_inputs=workflow_inputs,
+                                                             
supplied_inputs=inputs or {})
         execution.inputs = modeling_utils.merge_parameter_values(inputs,
                                                                  
workflow_inputs,
                                                                  
model_cls=models.Input)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51e4ed00/aria/parser/consumption/style.py
----------------------------------------------------------------------
diff --git a/aria/parser/consumption/style.py b/aria/parser/consumption/style.py
index 72892b9..1b52cd3 100644
--- a/aria/parser/consumption/style.py
+++ b/aria/parser/consumption/style.py
@@ -48,3 +48,7 @@ class Style(object):
     @staticmethod
     def meta(value):
         return Colored.green(value)
+
+    @staticmethod
+    def required(value):
+        return Colored.white(value)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51e4ed00/aria/parser/presentation/presentation.py
----------------------------------------------------------------------
diff --git a/aria/parser/presentation/presentation.py 
b/aria/parser/presentation/presentation.py
index fe55b05..fb71a1e 100644
--- a/aria/parser/presentation/presentation.py
+++ b/aria/parser/presentation/presentation.py
@@ -29,10 +29,11 @@ class Value(object):
     Encapsulates a typed value assignment.
     """
 
-    def __init__(self, type_name, value, description):
+    def __init__(self, type_name, value, description, required):
         self.type = deepcopy_with_locators(type_name)
         self.value = deepcopy_with_locators(value)
         self.description = deepcopy_with_locators(description)
+        self.required = deepcopy_with_locators(required)
 
     def _dump(self, context):
         if self.type is not None:
@@ -41,6 +42,8 @@ class Value(object):
             puts(context.style.literal(self.value))
         if self.description is not None:
             puts(context.style.meta(self.description))
+        if self.required is not None:
+            puts(context.style.required(self.required))
 
 
 class PresentationBase(HasCachedMethods):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51e4ed00/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py 
b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
index 4d8e1db..2620150 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
@@ -94,12 +94,10 @@ def create_service_template_model(context): # pylint: 
disable=too-many-locals,to
     # Topology template
     topology_template = context.presentation.get('service_template', 
'topology_template')
     if topology_template is not None:
-        create_parameter_models_from_values(model.inputs,
-                                            
topology_template._get_input_values(context),
-                                            model_cls=Input)
-        create_parameter_models_from_values(model.outputs,
-                                            
topology_template._get_output_values(context),
-                                            model_cls=Output)
+        model.inputs.update(
+            
create_input_models_from_values(topology_template._get_input_values(context)))
+        model.outputs.update(
+            
create_output_models_from_values(topology_template._get_output_values(context)))
 
     # Plugin specifications
     policies = context.presentation.get('service_template', 
'topology_template', 'policies')
@@ -171,12 +169,11 @@ def create_node_template_model(context, service_template, 
node_template):
     if node_template.description:
         model.description = node_template.description.value
 
-    create_parameter_models_from_values(model.properties,
-                                        
node_template._get_property_values(context),
-                                        model_cls=Property)
-    create_parameter_models_from_values(model.attributes,
-                                        
node_template._get_attribute_default_values(context),
-                                        model_cls=Attribute)
+    model.properties.update(create_property_models_from_values(
+        template_properties=node_template._get_property_values(context)))
+    model.attributes.update(create_attribute_models_from_values(
+        
template_attributes=node_template._get_attribute_default_values(context)))
+
     create_interface_template_models(context, service_template, 
model.interface_templates,
                                      node_template._get_interfaces(context))
 
@@ -221,11 +218,10 @@ def create_group_template_model(context, 
service_template, group):
     if group.description:
         model.description = group.description.value
 
-    create_parameter_models_from_values(model.properties, 
group._get_property_values(context),
-                                        model_cls=Property)
+    
model.properties.update(create_property_models_from_values(group._get_property_values(context)))
+
     create_interface_template_models(context, service_template, 
model.interface_templates,
                                      group._get_interfaces(context))
-
     members = group.members
     if members:
         for member in members:
@@ -245,8 +241,8 @@ def create_policy_template_model(context, service_template, 
policy):
     if policy.description:
         model.description = policy.description.value
 
-    create_parameter_models_from_values(model.properties, 
policy._get_property_values(context),
-                                        model_cls=Property)
+    model.properties.update(
+        
create_property_models_from_values(policy._get_property_values(context)))
 
     node_templates, groups = policy._get_targets(context)
     if node_templates:
@@ -356,19 +352,12 @@ def create_capability_template_model(context, 
service_template, capability):
 def create_interface_template_model(context, service_template, interface):
     interface_type = interface._get_type(context)
     interface_type = 
service_template.interface_types.get_descendant(interface_type._name)
-    model = InterfaceTemplate(name=interface._name,
-                              type=interface_type)
+    model = InterfaceTemplate(name=interface._name, type=interface_type)
 
     if interface_type.description:
         model.description = interface_type.description
 
-    inputs = interface.inputs
-    if inputs:
-        for input_name, the_input in inputs.iteritems():
-            model.inputs[input_name] = Input(name=input_name, # pylint: 
disable=unexpected-keyword-arg
-                                             type_name=the_input.value.type,
-                                             value=the_input.value.value,
-                                             
description=the_input.value.description)
+    create_parameter_models_from_assignments(model.inputs, interface.inputs, 
model_cls=Input)
 
     operations = interface.operations
     if operations:
@@ -430,14 +419,7 @@ def create_operation_template_model(context, 
service_template, operation):
             model.configurations[key] = Configuration.wrap(key, value,
                                                            
description='Operation configuration.')
 
-    inputs = operation.inputs
-    if inputs:
-        for input_name, the_input in inputs.iteritems():
-            model.inputs[input_name] = Input(name=input_name, # pylint: 
disable=unexpected-keyword-arg
-                                             type_name=the_input.value.type,
-                                             value=the_input.value.value,
-                                             
description=the_input.value.description)
-
+    create_parameter_models_from_assignments(model.inputs, operation.inputs, 
model_cls=Input)
     return model
 
 
@@ -462,8 +444,8 @@ def create_artifact_template_model(context, 
service_template, artifact):
             for k, v in credential.iteritems():
                 model.repository_credential[k] = v
 
-    create_parameter_models_from_values(model.properties, 
artifact._get_property_values(context),
-                                        model_cls=Property)
+    model.properties.update(
+        
create_property_models_from_values(artifact._get_property_values(context)))
 
     return model
 
@@ -526,10 +508,9 @@ def create_workflow_operation_template_model(context, 
service_template, policy):
         if prop_name == 'implementation':
             model.function = prop.value
         else:
-            model.inputs[prop_name] = Input(name=prop_name, # pylint: 
disable=unexpected-keyword-arg
-                                            type_name=prop.type,
-                                            value=prop.value,
-                                            description=prop.description)
+            input_model = create_parameter_model_from_value(prop, prop_name, 
model_cls=Input)
+            input_model.required = prop.required
+            model.inputs[prop_name] = input_model
 
     used_reserved_names = 
WORKFLOW_DECORATOR_RESERVED_ARGUMENTS.intersection(model.inputs.keys())
     if used_reserved_names:
@@ -539,7 +520,6 @@ def create_workflow_operation_template_model(context, 
service_template, policy):
                                       
string_list_as_string(used_reserved_names)),
                                   locator=policy._locator,
                                   level=Issue.EXTERNAL)
-
     return model
 
 
@@ -577,14 +557,50 @@ def create_types(context, root, types):
                         container.children.append(model)
 
 
-def create_parameter_models_from_values(properties, source_properties, 
model_cls):
-
-    if source_properties:
-        for property_name, prop in source_properties.iteritems():
-            properties[property_name] = model_cls(name=property_name,  # 
pylint: disable=unexpected-keyword-arg
-                                                  type_name=prop.type,
-                                                  value=prop.value,
-                                                  description=prop.description)
+def create_input_models_from_values(template_inputs):
+    model_inputs = {}
+    if template_inputs:
+        for template_input_name, template_input in template_inputs.iteritems():
+            model_input = create_parameter_model_from_value(template_input, 
template_input_name,
+                                                            model_cls=Input)
+            model_input.required = template_input.required
+            model_inputs[model_input.name] = model_input
+    return model_inputs
+
+def create_output_models_from_values(template_outputs):
+    model_outputs = {}
+    for template_output_name, template_output in template_outputs.iteritems():
+        model_outputs[template_output_name] = \
+            create_parameter_model_from_value(template_output,
+                                              template_output_name,
+                                              model_cls=Output)
+    return model_outputs
+
+
+def create_property_models_from_values(template_properties):
+    model_properties = {}
+    for template_property_name, template_property in 
template_properties.iteritems():
+        model_properties[template_property_name] = \
+            create_parameter_model_from_value(template_property,
+                                              template_property_name,
+                                              model_cls=Property)
+    return model_properties
+
+def create_attribute_models_from_values(template_attributes):
+    model_attributes = {}
+    for template_attribute_name, template_attribute in 
template_attributes.iteritems():
+        model_attributes[template_attribute_name] = \
+            create_parameter_model_from_value(template_attribute,
+                                              template_attribute_name,
+                                              model_cls=Attribute)
+    return model_attributes
+
+
+def create_parameter_model_from_value(template_parameter, 
template_parameter_name, model_cls):
+    return model_cls(name=template_parameter_name,
+                     type_name=template_parameter.type,
+                     value=template_parameter.value,
+                     description=template_parameter.description)
 
 
 def create_parameter_models_from_assignments(properties, source_properties, 
model_cls):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51e4ed00/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py 
b/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py
index fbb8280..9dee0f2 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py
@@ -438,10 +438,7 @@ def coerce_to_primitive(context, presentation, 
primitive_type, constraints, valu
 
         # Check constraints
         apply_constraints_to_value(context, presentation, constraints, value)
-    except ValueError as e:
-        report_issue_for_bad_format(context, presentation, primitive_type, 
value, aspect, e)
-        value = None
-    except TypeError as e:
+    except (ValueError, TypeError) as e:
         report_issue_for_bad_format(context, presentation, primitive_type, 
value, aspect, e)
         value = None
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51e4ed00/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py 
b/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py
index d5f447c..fffd5c1 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py
@@ -61,7 +61,7 @@ def get_and_override_input_definitions_from_type(context, 
presentation):
     inputs = OrderedDict()
 
     # Get inputs from type
-    the_type = presentation._get_type(context) # IntefaceType
+    the_type = presentation._get_type(context) # InterfaceType
     type_inputs = the_type._get_inputs(context) if the_type is not None else 
None
     if type_inputs:
         for input_name, type_input in type_inputs.iteritems():
@@ -213,9 +213,9 @@ def 
convert_interface_definition_from_type_to_raw_template(context, presentation
     raw = OrderedDict()
 
     # Copy default values for inputs
-    inputs = presentation._get_inputs(context)
-    if inputs is not None:
-        raw['inputs'] = convert_parameter_definitions_to_values(context, 
inputs)
+    interface_inputs = presentation._get_inputs(context)
+    if interface_inputs is not None:
+        raw['inputs'] = convert_parameter_definitions_to_values(context, 
interface_inputs)
 
     # Copy operations
     operations = presentation._get_operations(context)
@@ -481,6 +481,10 @@ def assign_raw_inputs(context, values, assignments, 
definitions, interface_name,
 
 def validate_required_inputs(context, presentation, assignment, definition, 
original_assignment,
                              interface_name, operation_name=None):
+    # The validation of the `required` field of inputs that belong to 
operations and interfaces
+    # (as opposed to topology template and workflow inputs) is done only in 
the parsing stage.
+    # This reasoning follows the TOSCA spirit, where anything that is declared 
as required in the
+    # type, must be assigned in the corresponding template.
     input_definitions = definition.inputs
     if input_definitions:
         for input_name, input_definition in input_definitions.iteritems():

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51e4ed00/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py 
b/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py
index 87c1a3b..f271b4d 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py
@@ -203,7 +203,8 @@ def coerce_parameter_value(context, presentation, 
definition, value, aspect=None
         type_name = getattr(definition, 'type', None)
     description = getattr(definition, 'description', None)
     description = description.value if description is not None else None
-    return Value(type_name, value, description)
+    required = getattr(definition, 'required', None)
+    return Value(type_name, value, description, required)
 
 
 def convert_parameter_definitions_to_values(context, definitions):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51e4ed00/tests/orchestrator/test_workflow_runner.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/test_workflow_runner.py 
b/tests/orchestrator/test_workflow_runner.py
index 30176ae..011c4cc 100644
--- a/tests/orchestrator/test_workflow_runner.py
+++ b/tests/orchestrator/test_workflow_runner.py
@@ -216,7 +216,7 @@ def test_execution_inputs_override_workflow_inputs(request):
 def test_execution_inputs_undeclared_inputs(request):
     mock_workflow = _setup_mock_workflow_in_service(request)
 
-    with pytest.raises(modeling_exceptions.UndeclaredParametersException):
+    with pytest.raises(modeling_exceptions.UndeclaredInputsException):
         _create_workflow_runner(request, mock_workflow, 
inputs={'undeclared_input': 'value'})
 
 
@@ -224,7 +224,7 @@ def test_execution_inputs_missing_required_inputs(request):
     mock_workflow = _setup_mock_workflow_in_service(
         request, inputs={'required_input': models.Input.wrap('required_input', 
value=None)})
 
-    with pytest.raises(modeling_exceptions.MissingRequiredParametersException):
+    with pytest.raises(modeling_exceptions.MissingRequiredInputsException):
         _create_workflow_runner(request, mock_workflow, inputs={})
 
 
@@ -238,7 +238,7 @@ def test_execution_inputs_wrong_type_inputs(request):
 
 def test_execution_inputs_builtin_workflow_with_inputs(request):
     # built-in workflows don't have inputs
-    with pytest.raises(modeling_exceptions.UndeclaredParametersException):
+    with pytest.raises(modeling_exceptions.UndeclaredInputsException):
         _create_workflow_runner(request, 'install', 
inputs={'undeclared_input': 'value'})
 
 
@@ -276,8 +276,8 @@ def service(model):
 def _setup_mock_workflow_in_service(request, inputs=None):
     # sets up a mock workflow as part of the service, including uploading
     # the workflow code to the service's dir on the resource storage
-    service = request.getfuncargvalue('service')
-    resource = request.getfuncargvalue('resource')
+    service = request.getfixturevalue('service')
+    resource = request.getfixturevalue('resource')
 
     source = tests_mock.workflow.__file__
     resource.service_template.upload(str(service.service_template.id), source)
@@ -299,10 +299,10 @@ def _setup_mock_workflow_in_service(request, inputs=None):
 def _create_workflow_runner(request, workflow_name, inputs=None, executor=None,
                             task_max_attempts=None, task_retry_interval=None):
     # helper method for instantiating a workflow runner
-    service_id = request.getfuncargvalue('service').id
-    model = request.getfuncargvalue('model')
-    resource = request.getfuncargvalue('resource')
-    plugin_manager = request.getfuncargvalue('plugin_manager')
+    service_id = request.getfixturevalue('service').id
+    model = request.getfixturevalue('model')
+    resource = request.getfixturevalue('resource')
+    plugin_manager = request.getfixturevalue('plugin_manager')
 
     # task configuration parameters can't be set to None, therefore only
     # passing those if they've been set by the test

Reply via email to