This is an automated email from the ASF dual-hosted git repository.

houshengbo pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/incubator-openwhisk-wskdeploy.git


The following commit(s) were added to refs/heads/master by this push:
     new 2876555  WIP: JSON Output support  (#607)
2876555 is described below

commit 2876555455464970cc73a7b2532054d246a66b4d
Author: Matt Rutkowski <mrutk...@us.ibm.com>
AuthorDate: Mon Oct 23 14:29:21 2017 -0500

    WIP: JSON Output support  (#607)
    
    JSON Output support
---
 parsers/manifest_parser.go                         | 253 ++++++++++++++++-----
 parsers/manifest_parser_test.go                    | 195 +++++++---------
 parsers/yamlparser.go                              |   2 +-
 tests/dat/manifest_bad_yaml_2.yaml                 |   8 -
 tests/dat/manifest_bad_yaml_3.yaml                 |   8 -
 tests/dat/manifest_bad_yaml_4.yaml                 |   6 -
 ...yaml => manifest_bad_yaml_invalid_comment.yaml} |   2 +-
 ...anifest_bad_yaml_invalid_key_mapping_value.yaml |   8 +
 .../dat/manifest_bad_yaml_invalid_package_key.yaml |   7 +
 tests/dat/manifest_bad_yaml_missing_root_key.yaml  |   6 +
 tests/dat/manifest_data_compose_triggers.yaml      |  12 +
 tests/dat/manifest_validate_params.yaml            |   2 +-
 tests/src/integration/common/wskdeploy.go          |   2 +-
 utils/flags.go                                     |   4 +-
 utils/misc_test.go                                 |  26 +--
 utils/wskdeployerror.go                            |  39 +++-
 16 files changed, 368 insertions(+), 212 deletions(-)

diff --git a/parsers/manifest_parser.go b/parsers/manifest_parser.go
index 08c5c99..c6ce6d2 100644
--- a/parsers/manifest_parser.go
+++ b/parsers/manifest_parser.go
@@ -376,16 +376,20 @@ func (dm *YAMLParser) ComposeActions(filePath string, 
actions map[string]Action,
                // set the name of the action (which is the key)
                action.Name = key
 
+               // Create action data object with CLI
+               wskaction := new(whisk.Action)
+               wskaction.Exec = new(whisk.Exec)
+
+               /*
+                *  Action.Function
+                */
                //set action.Function to action.Location
                //because Location is deprecated in Action entity
                if action.Function == "" && action.Location != "" {
                        action.Function = action.Location
                }
 
-               wskaction := new(whisk.Action)
                //bind action, and exposed URL
-
-               wskaction.Exec = new(whisk.Exec)
                if action.Function != "" {
                        filePath := strings.TrimRight(filePath, 
splitFilePath[len(splitFilePath)-1]) + action.Function
 
@@ -419,7 +423,7 @@ func (dm *YAMLParser) ComposeActions(filePath string, 
actions map[string]Action,
                                        kind = "nodejs:6"
                                        errStr := wski18n.T("Unsupported 
runtime type, set to nodejs")
                                        whisk.Debug(whisk.DbgWarn, errStr)
-                                       //add the user input kind here
+                                       // TODO() add the user input kind here 
if interactive
                                }
 
                                wskaction.Exec.Kind = kind
@@ -441,6 +445,9 @@ func (dm *YAMLParser) ComposeActions(filePath string, 
actions map[string]Action,
 
                }
 
+               /*
+                *  Action.Runtime
+                */
                if action.Runtime != "" {
                        if utils.CheckExistRuntime(action.Runtime, utils.Rts) {
                                wskaction.Exec.Kind = action.Runtime
@@ -458,11 +465,13 @@ func (dm *YAMLParser) ComposeActions(filePath string, 
actions map[string]Action,
                        wskaction.Exec.Main = action.Main
                }
 
+               /*
+                *  Action.Inputs
+                */
                keyValArr := make(whisk.KeyValueArr, 0)
                for name, param := range action.Inputs {
                        var keyVal whisk.KeyValue
                        keyVal.Key = name
-
                        keyVal.Value, errorParser = ResolveParameter(name, 
&param, filePath)
 
                        if errorParser != nil {
@@ -474,19 +483,51 @@ func (dm *YAMLParser) ComposeActions(filePath string, 
actions map[string]Action,
                        }
                }
 
+               // if we have successfully parser valid key/value parameters
                if len(keyValArr) > 0 {
                        wskaction.Parameters = keyValArr
                }
 
+               /*
+                *  Action.Outputs
+                */
+               keyValArr = make(whisk.KeyValueArr, 0)
+               for name, param := range action.Outputs {
+                       var keyVal whisk.KeyValue
+                       keyVal.Key = name
+                       keyVal.Value, errorParser = ResolveParameter(name, 
&param, filePath)
+
+                       // short circuit on error
+                       if errorParser != nil {
+                               return nil, errorParser
+                       }
+
+                       if keyVal.Value != nil {
+                               keyValArr = append(keyValArr, keyVal)
+                       }
+               }
+
+               // TODO{} add outputs as annotations (work to discuss 
officially supporting for compositions)
+               if len(keyValArr) > 0 {
+                       //wskaction.Annotations  // TBD
+               }
+
+               /*
+                *  Action.Annotations
+                */
                keyValArr = make(whisk.KeyValueArr, 0)
                for name, value := range action.Annotations {
                        var keyVal whisk.KeyValue
                        keyVal.Key = name
                        keyVal.Value = utils.GetEnvVar(value)
-
                        keyValArr = append(keyValArr, keyVal)
+                       // TODO{} Fix Annottions; they are not added to Action 
if web-export key is not present
+                       // Need to assure annotations are added/set even if 
web-export is not set on the action.
                }
 
+               /*
+                *  Web Export
+                */
                // only set the webaction when the annotations are not empty.
                if action.Webexport == "true" {
                        // TODO() why is this commented out?  we should now 
support annotations...
@@ -497,7 +538,9 @@ func (dm *YAMLParser) ComposeActions(filePath string, 
actions map[string]Action,
                        }
                }
 
-               //set limitations
+               /*
+                *  Action.Limits
+                */
                if action.Limits!=nil {
                        wsklimits :=  new(whisk.Limits)
                        if utils.LimitsTimeoutValidation(action.Limits.Timeout) 
{
@@ -769,97 +812,192 @@ func ResolveParamTypeFromValue(name string, value 
interface{}, filePath string)
        return paramType, err
 }
 
-// Resolve input parameter (i.e., type, value, default)
-// Note: parameter values may set later (overriddNen) by an (optional) 
Deployment file
-func ResolveParameter(paramName string, param *Parameter, filePath string) 
(interface{}, error) {
 
-       var errorParser error
-       var tempType string
-       // default parameter value to empty string
-       var value interface{} = ""
+/*
+    resolveSingleLineParameter assures that a Parameter's Type is correctly 
identified and set from its Value.
 
-       // Trace Parameter struct before any resolution
-       //dumpParameter(paramName, param, "BEFORE")
+    Additionally, this function:
+
+    - detects if the parameter value contains the name of a valid OpenWhisk 
parameter types. if so, the
+      - param.Type is set to detected OpenWhisk parameter type.
+      - param.Value is set to the zero (default) value for that OpenWhisk 
parameter type.
+ */
+func resolveSingleLineParameter(paramName string, param *Parameter, filePath 
string) (interface{}, error) {
+       var errorParser error
 
-       // Parameters can be single OR multi-line declarations which must be 
processed/validated differently
        if !param.multiline {
-               // we have a single-line parameter declaration
                // We need to identify parameter Type here for later validation
                param.Type, errorParser = ResolveParamTypeFromValue(paramName, 
param.Value, filePath)
 
                // In single-line format, the param's <value> can be a "Type 
name" and NOT an actual value.
                // if this is the case, we must detect it and set the value to 
the default for that type name.
-               if param.Value!=nil && param.Type == "string" {
+               if param.Value != nil && param.Type == "string" {
                        // The value is a <string>; now we must test if is the 
name of a known Type
-                       var tempValue = param.Value.(string)
-                       if isValidParameterType(tempValue) {
+                       if isValidParameterType(param.Value.(string)) {
                                // If the value is indeed the name of a Type, 
we must change BOTH its
                                // Type to be that type and its value to that 
Type's default value
-                               // (which happens later by setting it to nil 
here
                                param.Type = param.Value.(string)
-                               param.Value = nil
+                               param.Value = getTypeDefaultValue(param.Type)
+                               fmt.Printf("EXIT: Parameter [%s] type=[%v] 
value=[%v]\n", paramName, param.Type, param.Value)
                        }
                }
 
        } else {
-               // we have a multi-line parameter declaration
+               msgs := []string{"Parameter [" + paramName + "] is not 
single-line format."}
+               return param.Value, utils.NewParserErr(filePath, nil, msgs)
+       }
+
+       return param.Value, errorParser
+}
+
+/*
+    resolveMultiLineParameter assures that the values for Parameter Type and 
Value are properly set and are valid.
+
+    Additionally, this function:
+    - uses param.Default as param.Value if param.Value is not provided
+    - uses the actual param.Value data type for param.type if param.Type is 
not provided
+
+ */
+func resolveMultiLineParameter(paramName string, param *Parameter, filePath 
string) (interface{}, error) {
+       var errorParser error
+
+       if param.multiline {
+               var valueType string
 
                // if we do not have a value, but have a default, use it for 
the value
                if param.Value == nil && param.Default != nil {
                        param.Value = param.Default
                }
 
-               // if we also have a type at this point, verify value (and/or 
default) matches type, if not error
                // Note: if either the value or default is in conflict with the 
type then this is an error
-               tempType, errorParser = ResolveParamTypeFromValue(paramName, 
param.Value, filePath)
-
-               // if we do not have a value or default, but have a type, find 
its default and use it for the value
-               if param.Type != "" && !isValidParameterType(param.Type) {
-                       // TODO() - move string to i18n
-                       msgs := []string{"Parameter [" + paramName + "] has an 
invalid Type. [" + param.Type + "]"}
-                       return value, utils.NewParserErr(filePath, nil, msgs)
-               } else if param.Type == "" {
-                       param.Type = tempType
+               valueType, errorParser = ResolveParamTypeFromValue(paramName, 
param.Value, filePath)
+
+               // if we have a declared parameter Type, assure that it is a 
known value
+               if param.Type != "" {
+                       if !isValidParameterType(param.Type) {
+                               // TODO() - move string to i18n
+                               msgs := []string{"Parameter [" + paramName + "] 
has an invalid Type. [" + param.Type + "]"}
+                               return param.Value, 
utils.NewParserErr(filePath, nil, msgs)
+                       }
+               } else {
+                       // if we do not have a value for the Parameter Type, 
use the Parameter Value's Type
+                       param.Type = valueType
                }
+
+               // TODO{} if the declared and actual parameter type conflict, 
generate TypeMismatch error
+               //if param.Type != valueType{
+               //      errorParser = utils.NewParameterTypeMismatchError("", 
param.Type, valueType )
+               //}
+        } else {
+               msgs := []string{"Parameter [" + paramName + "] is not 
multiline format."}
+               return param.Value, utils.NewParserErr(filePath, nil, msgs)
        }
 
-       // Make sure the parameter's value is a valid, non-empty string and 
startsWith '$" (dollar) sign
-       value = utils.GetEnvVar(param.Value)
 
-       // JSON - Handle both cases, where value 1) is a string containing 
JSON, 2) is a map of JSON
+       return param.Value, errorParser
+}
 
-       // Case 1: if user set parameter type to 'json' and the value's type is 
a 'string'
-       if str, ok := value.(string); ok && param.Type == "json" {
-               var parsed interface{}
-               err := json.Unmarshal([]byte(str), &parsed)
-               if err == nil {
-                       fmt.Printf("EXIT: Parameter type=[%v] value=[%v]\n", 
param.Type, parsed)
-                       return parsed, err
+
+/*
+    resolveJSONParameter assure JSON data is converted to a 
map[string]{interface*} type.
+
+    This function handles the forms JSON data appears in:
+    1) a string containing JSON, which needs to be parsed into 
map[string]interface{}
+    2) is a map of JSON (but not a map[string]interface{}
+ */
+func resolveJSONParameter(paramName string, param *Parameter, value 
interface{}, filePath string) (interface{}, error) {
+       var errorParser error
+
+       if param.Type == "json" {
+               // Case 1: if user set parameter type to 'json' and the value's 
type is a 'string'
+               if str, ok := value.(string); ok {
+                       var parsed interface{}
+                       errParser := json.Unmarshal([]byte(str), &parsed)
+                       if errParser == nil {
+                               //fmt.Printf("EXIT: Parameter [%s] type=[%v] 
value=[%v]\n", paramName, param.Type, parsed)
+                               return parsed, errParser
+                       }
                }
+
+               // Case 2: value contains a map of JSON
+               // We must make sure the map type is map[string]interface{}; 
otherwise we cannot
+               // marshall it later on to serialize in the body of an HTTP 
request.
+               if( param.Value != nil && reflect.TypeOf(param.Value).Kind() == 
reflect.Map ) {
+                       if _, ok := param.Value.(map[interface{}]interface{}); 
ok {
+                               var temp map[string]interface{} =
+                                       
utils.ConvertInterfaceMap(param.Value.(map[interface{}]interface{}))
+                               //fmt.Printf("EXIT: Parameter [%s] type=[%v] 
value=[%v]\n", paramName, param.Type, temp)
+                               return temp, errorParser
+                       }
+               } // else TODO{}
+       } else {
+               msgs := []string{"Parameter [" + paramName + "] is not JSON 
format."}
+               return param.Value, utils.NewParserErr(filePath, nil, msgs)
        }
 
-       // Case 2: value contains a map of JSON
-       // We must make sure the map type is map[string]interface{}; otherwise 
we cannot
-       // marshall it later on to serialize in the body of an HTTP request.
-       if( param.Value != nil && reflect.TypeOf(param.Value).Kind() == 
reflect.Map ) {
-               if _, ok := param.Value.(map[interface{}]interface{}); ok {
-                       var temp map[string]interface{} =
-                               
utils.ConvertInterfaceMap(param.Value.(map[interface{}]interface{}))
-                       fmt.Printf("EXIT: Parameter type=[%v] value=[%v]\n", 
param.Type, temp)
-                       return temp, errorParser
-               }
+       return param.Value, errorParser
+}
+
+/*
+    ResolveParameter assures that the Parameter structure's values are 
correctly filled out for
+    further processing.  This includes special processing for
+
+    - single-line format parameters
+      - deriving missing param.Type from param.Value
+      - resolving case where param.Value contains a valid Parameter type name
+    - multi-line format parameters:
+      - assures that param.Value is set while taking into account param.Default
+      - validating param.Type
+
+    Note: parameter values may set later (overridden) by an (optional) 
Deployment file
+
+ */
+func ResolveParameter(paramName string, param *Parameter, filePath string) 
(interface{}, error) {
+
+       var errorParser error
+       // default parameter value to empty string
+       var value interface{} = ""
+
+       // Trace Parameter struct before any resolution
+       //dumpParameter(paramName, param, "BEFORE")
+
+       // Parameters can be single OR multi-line declarations which must be 
processed/validated differently
+       if !param.multiline {
+
+               // This function will assure that param.Value and param.Type 
are correctly set
+               value, errorParser = resolveSingleLineParameter(paramName, 
param, filePath)
+
+       } else {
+
+               value, errorParser = resolveMultiLineParameter(paramName, 
param, filePath)
        }
 
-       // Default to an empty string, do NOT error/terminate as Value may be 
provided later bu a Deployment file.
+       // String value pre-processing (interpolation)
+       // See if we have any Environment Variable replacement within the 
parameter's value
+
+       // Make sure the parameter's value is a valid, non-empty string
+       if ( param.Value != nil && param.Type == "string") {
+               // perform $ notation replacement on string if any exist
+               value = utils.GetEnvVar(param.Value)
+       }
+
+       // JSON - Handle both cases, where value 1) is a string containing 
JSON, 2) is a map of JSON
+       if param.Type == "json" {
+               value, errorParser = resolveJSONParameter(paramName, param, 
value, filePath)
+        }
+
+       // Default value to zero value for the Type
+       // Do NOT error/terminate as Value may be provided later by a 
Deployment file.
        if value == nil {
                value = getTypeDefaultValue(param.Type)
                // @TODO(): Need warning message here to warn of default usage, 
support for warnings (non-fatal)
+               //msgs := []string{"Parameter [" + paramName + "] is not 
multiline format."}
+               //return param.Value, utils.NewParserErr(filePath, nil, msgs)
        }
 
        // Trace Parameter struct after resolution
        //dumpParameter(paramName, param, "AFTER")
-       //fmt.Printf("EXIT: Parameter type=[%v] value=[%v]\n", param.Type, 
value)
-
+       //fmt.Printf("EXIT: Parameter [%s] type=[%v] value=[%v]\n", paramName, 
param.Type, value)
        return value, errorParser
 }
 
@@ -912,6 +1050,7 @@ func dumpParameter(paramName string, param *Parameter, 
separator string) {
        if param != nil {
                fmt.Printf("\t\tParameter.Description: [%s]\n", 
param.Description)
                fmt.Printf("\t\tParameter.Type: [%s]\n", param.Type)
+               fmt.Printf("\t\t--> Actual Type: [%T]\n", param.Value)
                fmt.Printf("\t\tParameter.Value: [%v]\n", param.Value)
                fmt.Printf("\t\tParameter.Default: [%v]\n", param.Default)
        }
diff --git a/parsers/manifest_parser_test.go b/parsers/manifest_parser_test.go
index 39c1a6a..0aef582 100644
--- a/parsers/manifest_parser_test.go
+++ b/parsers/manifest_parser_test.go
@@ -223,23 +223,8 @@ func TestUnmarshalForMissingPackage(t *testing.T) {
 
 /*
  Test 7: validate manifest_parser:ParseManifest() method for multiline 
parameters
- manifest_parser should be able to parse all different mutliline combinations 
of
- inputs section including:
-
- case 1: value only
- param:
-    value: <value>
- case 2: type only
- param:
-    type: <type>
- case 3: type and value only
- param:
-    type: <type>
-    value: <value>
- case 4: default value
- param:
-    type: <type>
-    default: <default value>
+ manifest_parser should be able to parse all different multiline combinations 
of
+ inputs section.
 */
 func TestParseManifestForMultiLineParams(t *testing.T) {
     // manifest file is located under ../tests folder
@@ -337,16 +322,18 @@ func TestParseManifestForMultiLineParams(t *testing.T) {
             }
         }
 
-        // validate outputs
-        // output payload is of type string and has a description
-        if payload, ok := action.Outputs["payload"]; ok {
-            p := payload.(map[interface{}]interface{})
-            expectedResult = "string"
-            actualResult = p["type"].(string)
-            assert.Equal(t, expectedResult, actualResult, "Expected " + 
expectedResult + " but got " + actualResult)
-            expectedResult = "parameter dump"
-            actualResult = p["description"].(string)
-            assert.Equal(t, expectedResult, actualResult, "Expected " + 
expectedResult + " but got " + actualResult)
+        // validate Outputs from this action
+        for output, param := range action.Outputs {
+            switch output {
+            case "payload":
+                expectedType := "string"
+                actualType := param.Type
+                assert.Equal(t, expectedType, actualType, "Expected Type: " + 
expectedType + ", but got: " + actualType)
+                expectedDesc := "parameter dump"
+                actualDesc := param.Description
+                assert.Equal(t, expectedDesc, actualDesc, "Expected " + 
expectedDesc + " but got " + actualDesc)
+
+            }
         }
     }
 }
@@ -387,7 +374,7 @@ func TestParseManifestForSingleLineParams(t *testing.T) {
         actualResult = strconv.FormatInt(int64(len(action.Inputs)), 10)
         assert.Equal(t, expectedResult, actualResult, "Expected " + 
expectedResult + " but got " + actualResult)
 
-        // validate inputs to this action
+        // validate Inputs to this action
         for input, param := range action.Inputs {
             switch input {
             case "param_simple_string":
@@ -445,16 +432,18 @@ func TestParseManifestForSingleLineParams(t *testing.T) {
             }
         }
 
-        // validate outputs
-        // output payload is of type string and has a description
-        if payload, ok := action.Outputs["payload"]; ok {
-            p := payload.(map[interface{}]interface{})
-            expectedResult = "string"
-            actualResult = p["type"].(string)
-            assert.Equal(t, expectedResult, actualResult, "Expected " + 
expectedResult + " but got " + actualResult)
-            expectedResult = "parameter dump"
-            actualResult = p["description"].(string)
-            assert.Equal(t, expectedResult, actualResult, "Expected " + 
expectedResult + " but got " + actualResult)
+        // validate Outputs from this action
+        for output, param := range action.Outputs {
+            switch output {
+            case "payload":
+                expectedResult = "string"
+                actualResult = param.Type
+                assert.Equal(t, expectedResult, actualResult, "Expected " + 
expectedResult + " but got " + actualResult)
+
+                expectedResult = "parameter dump"
+                actualResult = param.Description
+                assert.Equal(t, expectedResult, actualResult, "Expected " + 
expectedResult + " but got " + actualResult)
+            }
         }
     }
 }
@@ -530,6 +519,7 @@ func TestComposeActionsForInvalidRuntime(t *testing.T) {
             _, err := p.ComposeActionsFromAllPackages(m, tmpfile.Name())
             // (TODO) uncomment the following test case after issue #307 is 
fixed
             // (TODO) its failing right now as we are lacking check on invalid 
runtime
+            // TODO() 
https://github.com/apache/incubator-openwhisk-wskdeploy/issues/608
             // assert.NotNil(t, err, "Invalid runtime, ComposeActions should 
report an error")
             // (TODO) remove this print statement after uncommenting above 
test case
             fmt.Println(err)
@@ -545,7 +535,12 @@ func TestComposeActionsForSingleLineParams(t *testing.T) {
     manifestFile := "../tests/dat/manifest_validate_singleline_params.yaml"
     // read and parse manifest.yaml file
     p := NewYAMLParser()
-    m, _ := p.ParseManifest(manifestFile)
+    m, err := p.ParseManifest(manifestFile)
+
+    if err != nil {
+        assert.Fail(t, "Failed to parse manifest: " + manifestFile )
+    }
+
     actions, err := p.ComposeActionsFromAllPackages(m, manifestFile)
 
     if err == nil {
@@ -699,7 +694,12 @@ func TestComposeActionsForMultiLineParams(t *testing.T) {
     manifestFile := "../tests/dat/manifest_validate_multiline_params.yaml"
     // read and parse manifest.yaml file
     p := NewYAMLParser()
-    m, _ := p.ParseManifest(manifestFile)
+    m, err := p.ParseManifest(manifestFile)
+
+    if err != nil {
+        assert.Fail(t, "Failed to parse manifest: " + manifestFile )
+    }
+
     actions, err := p.ComposeActionsFromAllPackages(m, manifestFile)
 
     if err == nil {
@@ -954,7 +954,11 @@ func TestParseManifestForJSONParams(t *testing.T) {
     // manifest file is located under ../tests folder
     manifestFile := "../tests/dat/manifest_validate_json_params.yaml"
     // read and parse manifest.yaml file
-    m, _ := NewYAMLParser().ParseManifest(manifestFile)
+    m, err := NewYAMLParser().ParseManifest(manifestFile)
+
+    if err != nil {
+        assert.Fail(t, "Failed to parse manifest: " + manifestFile )
+    }
 
     // validate package name should be "validate"
     packageName := "validate_json"
@@ -1017,12 +1021,15 @@ func TestParseManifestForJSONParams(t *testing.T) {
             }
         }
 
-        // TODO{} We do not yet support json outputs
-        // validate outputs
-        // output payload is of type string and has a description
-        //if payload, ok := action.Outputs["fellowship"]; ok {
-        //    p := payload.(map[interface{}]interface{})
-        //}
+        // validate Outputs from this action
+        for output, param := range action.Outputs {
+            switch output {
+            case "fellowship":
+                expectedType := "json"
+                actualType := param.Type
+                assert.Equal(t, expectedType, actualType, "Expected Type: " + 
expectedType + ", but got: " + actualType)
+            }
+        }
     }
 }
 
@@ -1108,30 +1115,15 @@ func TestComposeSequences(t *testing.T) {
 }
 
 func TestComposeTriggers(t *testing.T) {
-    data := `package:
-  name: helloworld
-  triggers:
-    trigger1:
-      inputs:
-        name: string
-        place: string
-    trigger2:
-      feed: myfeed
-      inputs:
-        name: myname
-        place: myplace`
-    tmpfile, err := _createTmpfile(data, "manifest_parser_test_")
+    // read and parse manifest.yaml file located under ../tests folder
+    manifestFile := "../tests/dat/manifest_data_compose_triggers.yaml"
+    p := NewYAMLParser()
+    m, err := p.ParseManifest(manifestFile)
     if err != nil {
-        assert.Fail(t, "Failed to create temp file")
+        assert.Fail(t, "Failed to parse manifest: " + manifestFile )
     }
-    defer func() {
-        tmpfile.Close()
-        os.Remove(tmpfile.Name())
-    }()
-    // read and parse manifest.yaml file
-    p := NewYAMLParser()
-    m, _ := p.ParseManifest(tmpfile.Name())
-    triggerList, err := p.ComposeTriggersFromAllPackages(m, tmpfile.Name())
+
+    triggerList, err := p.ComposeTriggersFromAllPackages(m, manifestFile)
     if err != nil {
         assert.Fail(t, "Failed to compose trigger")
     }
@@ -1315,65 +1307,42 @@ func TestComposeDependencies(t *testing.T) {
     }
 }
 
-func TestInvalidKeyManifestYaml(t *testing.T) {
-    data := `package:
-  name: helloWorldTriggerRule
-  version: 1.0
-  invalidKey: test
-  license: Apache-2.0`
-    tmpfile, err := _createTmpfile(data, "manifest_parser_test_")
-    if err != nil {
-        assert.Fail(t, "Failed to create temp file")
-    }
-    defer func() {
-        tmpfile.Close()
-        os.Remove(tmpfile.Name())
-    }()
+func TestBadYAMLInvalidPackageKeyInManifest(t *testing.T) {
+    // read and parse manifest.yaml file located under ../tests folder
     p := NewYAMLParser()
-    _, err = p.ParseManifest(tmpfile.Name())
+    _, err := 
p.ParseManifest("../tests/dat/manifest_bad_yaml_invalid_package_key.yaml")
+
     assert.NotNil(t, err)
     // go-yaml/yaml prints the wrong line number for mapping values. It should 
be 4.
     assert.Contains(t, err.Error(), "line 2: field invalidKey not found in 
struct parsers.Package")
 }
 
-func TestMappingValueManifestYaml(t *testing.T) {
-    data := `package:
-  name: helloWorldTriggerRule
-  version: 1.0
-  license: Apache-2.0
-    actions: test`
-    tmpfile, err := _createTmpfile(data, "manifest_parser_test_")
-    if err != nil {
-        assert.Fail(t, "Failed to create temp file")
-    }
-    defer func() {
-        tmpfile.Close()
-        os.Remove(tmpfile.Name())
-    }()
+func TestBadYAMLInvalidKeyMappingValueInManifest(t *testing.T) {
+    // read and parse manifest.yaml file located under ../tests folder
     p := NewYAMLParser()
-    _, err = p.ParseManifest(tmpfile.Name())
+    _, err := 
p.ParseManifest("../tests/dat/manifest_bad_yaml_invalid_key_mapping_value.yaml")
+
     assert.NotNil(t, err)
     // go-yaml/yaml prints the wrong line number for mapping values. It should 
be 5.
     assert.Contains(t, err.Error(), "line 4: mapping values are not allowed in 
this context")
 }
 
-func TestMissingRootValueManifestYaml(t *testing.T) {
-    data := `actions:
-  helloNodejs:
-    function: actions/hello.js`
-    tmpfile, err := _createTmpfile(data, "manifest_parser_test_")
-    if err != nil {
-        assert.Fail(t, "Failed to create temp file")
-    }
-    defer func() {
-        tmpfile.Close()
-        os.Remove(tmpfile.Name())
-    }()
+func TestBadYAMLMissingRootKeyInManifest(t *testing.T) {
+    // read and parse manifest.yaml file located under ../tests folder
     p := NewYAMLParser()
-    _, err = p.ParseManifest(tmpfile.Name())
+    _, err := 
p.ParseManifest("../tests/dat/manifest_bad_yaml_missing_root_key.yaml")
+
     assert.NotNil(t, err)
     assert.Contains(t, err.Error(), "line 1: field actions not found in struct 
parsers.YAML")
+}
+
+func TestBadYAMLInvalidCommentInManifest(t *testing.T) {
+    // read and parse manifest.yaml file located under ../tests folder
+    p := NewYAMLParser()
+    _, err := 
p.ParseManifest("../tests/dat/manifest_bad_yaml_invalid_comment.yaml")
 
+    assert.NotNil(t, err)
+    assert.Contains(t, err.Error(), "line 13: could not find expected ':'")
 }
 
 // validate manifest_parser:Unmarshal() method for package in manifest YAML
@@ -1552,7 +1521,7 @@ func TestParseYAML_param(t *testing.T) {
                panic(err)
        }
 
-        packageName := "manifest6"
+        packageName := "validateParams"
 
        assert.Equal(t, 1, len(manifest.Packages[packageName].Actions), "Get 
action list failed.")
        for action_name := range manifest.Packages[packageName].Actions {
diff --git a/parsers/yamlparser.go b/parsers/yamlparser.go
index 4b7a2db..974ce5b 100644
--- a/parsers/yamlparser.go
+++ b/parsers/yamlparser.go
@@ -58,7 +58,7 @@ type Action struct {
        Namespace  string                 `yaml:"namespace"`  //used in 
deployment.yaml
        Credential string                 `yaml:"credential"` //used in 
deployment.yaml
        Inputs     map[string]Parameter   `yaml:"inputs"`     //used in both 
manifest.yaml and deployment.yaml
-       Outputs    map[string]interface{} `yaml:"outputs"`    //used in 
manifest.yaml
+       Outputs    map[string]Parameter   `yaml:"outputs"`    //used in 
manifest.yaml
        //mapping to wsk.Action.Name
        Name        string
        Annotations map[string]interface{} `yaml:"annotations,omitempty"`
diff --git a/tests/dat/manifest_bad_yaml_2.yaml 
b/tests/dat/manifest_bad_yaml_2.yaml
deleted file mode 100644
index bd4d895..0000000
--- a/tests/dat/manifest_bad_yaml_2.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-package:
-  name: helloWorldTriggerRule
-  version: 1.0
-  invalidKey: test
-  license: Apache-2.0
-  invalidKey2: test
-
-# go-yaml/yaml prints the wrong line number for mapping values. It should be 4.
diff --git a/tests/dat/manifest_bad_yaml_3.yaml 
b/tests/dat/manifest_bad_yaml_3.yaml
deleted file mode 100644
index bcffb6e..0000000
--- a/tests/dat/manifest_bad_yaml_3.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-packages:
-  helloWorldTriggerRule:
-    version: 1.0
-    license: Apache-2.0
-      actions: test
-
-# go-yaml/yaml prints the wrong line number for mapping values. It should be 5.
-
diff --git a/tests/dat/manifest_bad_yaml_4.yaml 
b/tests/dat/manifest_bad_yaml_4.yaml
deleted file mode 100644
index da73f3e..0000000
--- a/tests/dat/manifest_bad_yaml_4.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-actions:
-  helloNodejs:
-    function: actions/hello.js
-
-# go-yaml/yaml prints the wrong line number for mapping values. It should be 5.
-
diff --git a/tests/dat/manifest_bad_yaml.yaml 
b/tests/dat/manifest_bad_yaml_invalid_comment.yaml
similarity index 89%
rename from tests/dat/manifest_bad_yaml.yaml
rename to tests/dat/manifest_bad_yaml_invalid_comment.yaml
index e4fcf70..e1330e5 100644
--- a/tests/dat/manifest_bad_yaml.yaml
+++ b/tests/dat/manifest_bad_yaml_invalid_comment.yaml
@@ -1,5 +1,5 @@
 packages:
-  BadYAML:
+  testBadYAMLInvalidCommentInManifest:
     actions:
       # helloworld action in NodeJS
       helloNodejs:
diff --git a/tests/dat/manifest_bad_yaml_invalid_key_mapping_value.yaml 
b/tests/dat/manifest_bad_yaml_invalid_key_mapping_value.yaml
new file mode 100644
index 0000000..5d0cd70
--- /dev/null
+++ b/tests/dat/manifest_bad_yaml_invalid_key_mapping_value.yaml
@@ -0,0 +1,8 @@
+packages:
+  testBadYAMLInvalidKeyMappingValueInManifest:
+    version: 1.0
+    license: Apache-2.0
+      actions: test
+
+# go-yaml/yaml "line 4: mapping values are not allowed in this context".
+
diff --git a/tests/dat/manifest_bad_yaml_invalid_package_key.yaml 
b/tests/dat/manifest_bad_yaml_invalid_package_key.yaml
new file mode 100644
index 0000000..2d74f7a
--- /dev/null
+++ b/tests/dat/manifest_bad_yaml_invalid_package_key.yaml
@@ -0,0 +1,7 @@
+package:
+  name: testBadYAMLInvalidPackageKeyInManifest
+  version: 1.0
+  invalidKey: test
+  license: Apache-2.0
+
+# go-yaml/yaml "line 2: field invalidKey not found in struct parsers.Package".
diff --git a/tests/dat/manifest_bad_yaml_missing_root_key.yaml 
b/tests/dat/manifest_bad_yaml_missing_root_key.yaml
new file mode 100644
index 0000000..b8d3d0b
--- /dev/null
+++ b/tests/dat/manifest_bad_yaml_missing_root_key.yaml
@@ -0,0 +1,6 @@
+actions:
+  testBadYAMLMissingRootKeyInManifest:
+    function: actions/hello.js
+
+# go-yaml/yaml "line 1: field actions not found in struct parsers.YAML".
+
diff --git a/tests/dat/manifest_data_compose_triggers.yaml 
b/tests/dat/manifest_data_compose_triggers.yaml
new file mode 100644
index 0000000..1d99fe9
--- /dev/null
+++ b/tests/dat/manifest_data_compose_triggers.yaml
@@ -0,0 +1,12 @@
+package:
+  name: trigger_compose
+  triggers:
+    trigger1:
+      inputs:
+        name: string
+        place: string
+    trigger2:
+      feed: myfeed
+      inputs:
+        name: myname
+        place: myplace
diff --git a/tests/dat/manifest_validate_params.yaml 
b/tests/dat/manifest_validate_params.yaml
index 584b7ed..0bd226b 100644
--- a/tests/dat/manifest_validate_params.yaml
+++ b/tests/dat/manifest_validate_params.yaml
@@ -1,5 +1,5 @@
 packages:
-  manifest6:
+  validateParams:
     actions:
       action1:
         inputs:
diff --git a/tests/src/integration/common/wskdeploy.go 
b/tests/src/integration/common/wskdeploy.go
index b142789..190353a 100644
--- a/tests/src/integration/common/wskdeploy.go
+++ b/tests/src/integration/common/wskdeploy.go
@@ -200,7 +200,7 @@ func (wskdeploy *Wskdeploy) 
GetDeploymentObjects(manifestPath string, deployment
                utils.Rts = utils.DefaultRts
        }
 
-       //invokce ConstructDeploymentPlan to create the in memory objects for 
deployment
+       //invoke ConstructDeploymentPlan to create the in memory objects for 
deployment
        err = deployer.ConstructDeploymentPlan()
        if err != nil {
                return nil,err
diff --git a/utils/flags.go b/utils/flags.go
index 03390ad..7b35856 100644
--- a/utils/flags.go
+++ b/utils/flags.go
@@ -33,8 +33,8 @@ var Flags struct {
        UseDefaults     bool
        UseInteractive  bool
        Strict          bool   // strict flag to support user defined runtime 
version.
-    Key  string
-    Cert    string
+       Key             string
+       Cert            string
 
        //action flag definition
        //from go cli
diff --git a/utils/misc_test.go b/utils/misc_test.go
index 638468c..94203ad 100644
--- a/utils/misc_test.go
+++ b/utils/misc_test.go
@@ -82,20 +82,20 @@ func TestDependencies(t *testing.T) {
 
 func TestParseOpenWhisk(t *testing.T) {
        openwhiskHost := "https://openwhisk.ng.bluemix.net";
-    openwhisk, err := ParseOpenWhisk(openwhiskHost)
-    assert.Equal(t, nil, err, "parse openwhisk info error happened.")
-    converted := ConvertToMap(openwhisk)
-    assert.Equal(t, 1, len(converted["nodejs"]), "not expected length")
-    assert.Equal(t, 1, len(converted["php"]),  "not expected length")
-    assert.Equal(t, 1, len(converted["java"]), "not expected length")
-    assert.Equal(t, 3, len(converted["python"]), "not expected length")
-    assert.Equal(t, 2, len(converted["swift"]), "not expected length")
+       openwhisk, err := ParseOpenWhisk(openwhiskHost)
+       assert.Equal(t, nil, err, "parse openwhisk info error happened.")
+       converted := ConvertToMap(openwhisk)
+       assert.Equal(t, 1, len(converted["nodejs"]), "not expected length")
+       assert.Equal(t, 1, len(converted["php"]),  "not expected length")
+       assert.Equal(t, 1, len(converted["java"]), "not expected length")
+       assert.Equal(t, 3, len(converted["python"]), "not expected length")
+       assert.Equal(t, 2, len(converted["swift"]), "not expected length")
 }
 
 func TestNewZipWritter(t *testing.T) {
-    filePath := "../tests/src/integration/zipaction/actions/cat"
-    zipName := filePath + ".zip"
-    err := NewZipWritter(filePath, zipName).Zip()
-    defer os.Remove(zipName)
-    assert.Equal(t, nil, err, "zip folder error happened.")
+       filePath := "../tests/src/integration/zipaction/actions/cat"
+       zipName := filePath + ".zip"
+       err := NewZipWritter(filePath, zipName).Zip()
+       defer os.Remove(zipName)
+       assert.Equal(t, nil, err, "zip folder error happened.")
 }
diff --git a/utils/wskdeployerror.go b/utils/wskdeployerror.go
index d699184..6613384 100644
--- a/utils/wskdeployerror.go
+++ b/utils/wskdeployerror.go
@@ -29,6 +29,7 @@ const (
     INVALID_YAML_INPUT = "Invalid input of Yaml file"
     INVALID_YAML_FORMAT = "Invalid input of Yaml format"
     OPENWHISK_CLIENT_ERROR = "OpenWhisk Client Error"
+    PARAMETER_TYPE_MISMATCH = "Parameter type mismatch error"
     MANIFEST_NOT_FOUND = INVALID_YAML_INPUT  // TODO{} This should be a unique 
message.
     UNKNOWN = "Unknown"
     UNKNOWN_VALUE = "Unknown value"
@@ -89,7 +90,10 @@ func NewErrorManifestFileNotFound(errMessage string) 
*ErrorManifestFileNotFound
 }
 
 func (e *ErrorManifestFileNotFound) Error() string {
-    return fmt.Sprintf("%s [%d]: %s =====> %s\n", e.FileName, e.LineNum, 
e.errorType, e.Message)
+    if e.errorType == "" {
+        return fmt.Sprintf("%s [%d]: %s\n", e.FileName, e.LineNum, e.Message)
+    }
+    return fmt.Sprintf("%s [%d]: %s ==> %s\n", e.FileName, e.LineNum, 
e.errorType, e.Message)
 }
 
 type InputYamlFileError struct {
@@ -113,6 +117,9 @@ func (e *InputYamlFileError) SetErrorType(errorType string) 
{
 }
 
 func (e *InputYamlFileError) Error() string {
+    if e.errorType == "" {
+        return fmt.Sprintf("%s [%d]: %s\n", e.FileName, e.LineNum, e.Message)
+    }
     return fmt.Sprintf("%s [%d]: %s %s\n", e.FileName, e.LineNum, e.errorType, 
e.Message)
 }
 
@@ -199,3 +206,33 @@ func (e *ParserErr) Error() string {
     }
     return fmt.Sprintf("\n==> %s [%d]: Failed to parse the yaml file: %s: 
\n%s", fn, e.LineNum, e.YamlFile, strings.Join(result, "\n"))
 }
+
+type ParameterTypeMismatchError struct {
+    BaseErr
+    errorType string
+    expectedType string
+    actualType string
+}
+
+func (e *ParameterTypeMismatchError) Error() string {
+    if e.errorType == "" {
+        return fmt.Sprintf("%s [%d]: %s\n", e.FileName, e.LineNum, e.Message)
+    }
+    return fmt.Sprintf("%s [%d]: %s ==> %s\n", e.FileName, e.LineNum, 
e.errorType, e.Message)
+}
+
+func NewParameterTypeMismatchError(errMessage string, expectedType string, 
actualType string) *ParameterTypeMismatchError {
+    _, fn, lineNum, _ := runtime.Caller(1)
+    var err = &ParameterTypeMismatchError{
+        // TODO{} add i18n
+        //errorType: wski18n.T(PARAMETER_TYPE_MISMATCH),
+        errorType: PARAMETER_TYPE_MISMATCH,
+        expectedType: expectedType,
+        actualType: actualType,
+    }
+
+    err.SetFileName(filepath.Base(fn))
+    err.SetLineNum(lineNum)
+    err.SetMessage(errMessage)
+    return err
+}

-- 
To stop receiving notification emails like this one, please contact
['"commits@openwhisk.apache.org" <commits@openwhisk.apache.org>'].

Reply via email to