You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by ho...@apache.org on 2017/10/13 15:27:19 UTC
[incubator-openwhisk-wskdeploy] branch master updated: Add Json
support for Input parameters (#601)
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 a079773 Add Json support for Input parameters (#601)
a079773 is described below
commit a079773c5ef4dabc7c09301f79d10ef0562df4cd
Author: Matt Rutkowski <mr...@us.ibm.com>
AuthorDate: Fri Oct 13 11:27:17 2017 -0400
Add Json support for Input parameters (#601)
* Initial test manifest and enable json on list of valid types.
* Initial test manifest and enable json on list of valid types.
* Add json type as a Golang map.
* Enhance error line number reporting on parameter type errors.
* Add json as valid type.
* Add json as valid type.
* Add json as valid type.
* Add json as valid type.
* Add json as valid type.
* Add json as valid type.
* Add json as valid type.
* Handle bothe cases of Map values for JSON type.
* Handle bothe cases of Map values for JSON type.
* Handle bothe cases of Map values for JSON type.
* Handle bothe cases of Map values for JSON type.
* Handle bothe cases of Map values for JSON type.
* Handle bothe cases of Map values for JSON type.
* Handle both cases of Map values for JSON type.
* Handle both cases of Map values for JSON type.
* Handle both cases of Map values for JSON type.
* Handle both cases of Map values for JSON type.
* Handle both cases of Map values for JSON type.
* Handle both cases of Map values for JSON type.
* Handle both cases of Map values for JSON type.
* Handle both cases of Map values for JSON type.
* Handle both cases of Map values for JSON type.
* Handle both cases of Map values for JSON type.
* Handle both cases of Map values for JSON type.
* Handle both cases of Map values for JSON type.
* Handle both cases of Map values for JSON type.
* Handle both cases of Map values for JSON type.
* Handle both cases of Map values for JSON type.
* Handle both cases of Map values for JSON type.
---
cmd/root.go | 7 +-
deployers/servicedeployer.go | 60 ++++++++++-----
parsers/deploy_parser.go | 19 ++---
parsers/deploy_parser_test.go | 6 +-
parsers/manifest_parser.go | 111 +++++++++++++++------------
parsers/manifest_parser_test.go | 83 +++++++++++++++++++-
parsers/yamlparser.go | 6 +-
tests/dat/manifest_bad_yaml_2.yaml | 8 ++
tests/dat/manifest_bad_yaml_3.yaml | 8 ++
tests/dat/manifest_bad_yaml_4.yaml | 6 ++
tests/dat/manifest_validate_json_params.yaml | 33 ++++++++
utils/conversion.go | 54 +++++++++++++
utils/dependencies.go | 1 -
utils/misc.go | 9 +--
utils/wskdeployerror.go | 26 ++++---
15 files changed, 325 insertions(+), 112 deletions(-)
diff --git a/cmd/root.go b/cmd/root.go
index 03ab905..331621c 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -314,9 +314,10 @@ func Undeploy() error {
setSupportedRuntimes(clientConfig.Host)
verifiedPlan, err := deployer.ConstructUnDeploymentPlan()
- if err != nil {
- return err
- }
+ if err != nil {
+ return err
+ }
+
err = deployer.UnDeploy(verifiedPlan)
if err != nil {
return err
diff --git a/deployers/servicedeployer.go b/deployers/servicedeployer.go
index 509f4d4..c340a3e 100644
--- a/deployers/servicedeployer.go
+++ b/deployers/servicedeployer.go
@@ -30,6 +30,7 @@ import (
"github.com/apache/incubator-openwhisk-wskdeploy/parsers"
"github.com/apache/incubator-openwhisk-wskdeploy/utils"
"github.com/apache/incubator-openwhisk-wskdeploy/wski18n"
+ "reflect"
)
type DeploymentApplication struct {
@@ -138,12 +139,12 @@ func (deployer *ServiceDeployer) ConstructDeploymentPlan() error {
return err
}
- applicationName := ""
- if len(manifest.Application.Packages) != 0 {
- applicationName = manifest.Application.Name
- }
+ applicationName := ""
+ if len(manifest.Application.Packages) != 0 {
+ applicationName = manifest.Application.Name
+ }
- // process deploymet file
+ // process deployment file
if utils.FileExists(deployer.DeploymentPath) {
var deploymentReader = NewDeploymentReader(deployer)
err = deploymentReader.HandleYaml()
@@ -151,16 +152,16 @@ func (deployer *ServiceDeployer) ConstructDeploymentPlan() error {
if err != nil {
return err
}
- // compare the name of the application
- if len(deploymentReader.DeploymentDescriptor.Application.Packages) != 0 && len(applicationName) != 0 {
- appNameDeploy := deploymentReader.DeploymentDescriptor.Application.Name
- if appNameDeploy != applicationName {
- errorString := wski18n.T("The name of the application {{.appNameDeploy}} in deployment file at [{{.deploymentFile}}] does not match the name of the application {{.appNameManifest}}} in manifest file at [{{.manifestFile}}].",
- map[string]interface{}{"appNameDeploy": appNameDeploy, "deploymentFile": deployer.DeploymentPath,
- "appNameManifest": applicationName, "manifestFile": deployer.ManifestPath })
- return utils.NewInputYamlFormatError(errorString)
- }
- }
+ // compare the name of the application
+ if len(deploymentReader.DeploymentDescriptor.Application.Packages) != 0 && len(applicationName) != 0 {
+ appNameDeploy := deploymentReader.DeploymentDescriptor.Application.Name
+ if appNameDeploy != applicationName {
+ errorString := wski18n.T("The name of the application {{.appNameDeploy}} in deployment file at [{{.deploymentFile}}] does not match the name of the application {{.appNameManifest}}} in manifest file at [{{.manifestFile}}].",
+ map[string]interface{}{"appNameDeploy": appNameDeploy, "deploymentFile": deployer.DeploymentPath,
+ "appNameManifest": applicationName, "manifestFile": deployer.ManifestPath })
+ return utils.NewInputYamlFormatError(errorString)
+ }
+ }
deploymentReader.BindAssets()
}
@@ -1048,7 +1049,7 @@ func (deployer *ServiceDeployer) printDeploymentAssets(assets *DeploymentApplica
for _, p := range pack.Package.Parameters {
jsonValue, err := utils.PrettyJSON(p.Value)
if err != nil {
- fmt.Printf(" - %s : %s\n", p.Key, "Unknown value")
+ fmt.Printf(" - %s : %s\n", p.Key, utils.UNKNOWN_VALUE)
} else {
fmt.Printf(" - %s : %v\n", p.Key, jsonValue)
}
@@ -1068,12 +1069,29 @@ func (deployer *ServiceDeployer) printDeploymentAssets(assets *DeploymentApplica
utils.PrintOpenWhiskOutputln(" * action: " + action.Action.Name)
utils.PrintOpenWhiskOutputln(" bindings: ")
for _, p := range action.Action.Parameters {
- jsonValue, err := utils.PrettyJSON(p.Value)
- if err != nil {
- fmt.Printf(" - %s : %s\n", p.Key, "Unknown value")
+
+ if( reflect.TypeOf(p.Value).Kind() == reflect.Map ) {
+ if _, ok := p.Value.(map[interface{}]interface{}); ok {
+ var temp map[string]interface{} =
+ utils.ConvertInterfaceMap(p.Value.(map[interface{}]interface{}))
+ fmt.Printf(" - %s : %v\n", p.Key, temp)
+ } else {
+ jsonValue,err := utils.PrettyJSON(p.Value)
+ if err != nil {
+ fmt.Printf(" - %s : %s\n", p.Key, utils.UNKNOWN_VALUE)
+ } else {
+ fmt.Printf(" - %s : %v\n", p.Key, jsonValue)
+ }
+ }
} else {
- fmt.Printf(" - %s : %v\n", p.Key, jsonValue)
+ jsonValue, err := utils.PrettyJSON(p.Value)
+ if err != nil {
+ fmt.Printf(" - %s : %s\n", p.Key, utils.UNKNOWN_VALUE)
+ } else {
+ fmt.Printf(" - %s : %v\n", p.Key, jsonValue)
+ }
}
+
}
utils.PrintOpenWhiskOutputln(" annotations: ")
for _, p := range action.Action.Annotations {
@@ -1098,7 +1116,7 @@ func (deployer *ServiceDeployer) printDeploymentAssets(assets *DeploymentApplica
for _, p := range trigger.Parameters {
jsonValue, err := utils.PrettyJSON(p.Value)
if err != nil {
- fmt.Printf(" - %s : %s\n", p.Key, "Unknown value")
+ fmt.Printf(" - %s : %s\n", p.Key, utils.UNKNOWN_VALUE)
} else {
fmt.Printf(" - %s : %v\n", p.Key, jsonValue)
}
diff --git a/parsers/deploy_parser.go b/parsers/deploy_parser.go
index 316655e..148d6ce 100644
--- a/parsers/deploy_parser.go
+++ b/parsers/deploy_parser.go
@@ -59,19 +59,14 @@ func (dm *YAMLParser) ParseDeployment(deploymentPath string) (*YAML, error) {
func (dm *YAMLParser) convertErrorToLinesMsgs(errorString string) (lines []string, msgs []string) {
strs := strings.Split(errorString, "\n")
for i := 0; i < len(strs); i++ {
- errMsg := strings.TrimSpace(strs[i])
- if strings.Contains(errMsg, utils.LINE) {
- s := strings.Split(errMsg, utils.LINE)
- lineMsg := s[1]
- line := strings.Split(lineMsg, ":")
- if (len(line) == 2) {
- lines = append(lines, strings.TrimSpace(line[0]))
- msgs = append(msgs, line[1])
- continue
- }
- }
+ var errorMsg string
+ if strings.Contains(strs[i], utils.LINE) {
+ errorMsg = strings.Replace(strs[i], utils.LINE, "(on or near) "+utils.LINE, 1)
+ } else {
+ errorMsg = strs[i]
+ }
lines = append(lines, utils.UNKNOWN)
- msgs = append(msgs, errMsg)
+ msgs = append(msgs, strings.TrimSpace(errorMsg))
}
return
}
diff --git a/parsers/deploy_parser_test.go b/parsers/deploy_parser_test.go
index 2dea01b..e98666f 100644
--- a/parsers/deploy_parser_test.go
+++ b/parsers/deploy_parser_test.go
@@ -55,7 +55,7 @@ func TestInvalidKeyDeploymentYaml(t *testing.T) {
_, err = p.ParseDeployment(tmpfile.Name())
assert.NotNil(t, err)
// go-yaml/yaml prints the wrong line number for mapping values. It should be 3.
- assert.Contains(t, err.Error(), "field invalidKey not found in struct parsers.Application: Line 2, its neighbor lines, or the lines on the same level")
+ assert.Contains(t, err.Error(), "line 2: field invalidKey not found in struct parsers.Application")
}
func TestMappingValueDeploymentYaml(t *testing.T) {
@@ -74,7 +74,7 @@ func TestMappingValueDeploymentYaml(t *testing.T) {
_, err = p.ParseDeployment(tmpfile.Name())
assert.NotNil(t, err)
// go-yaml/yaml prints the wrong line number for mapping values. It should be 3.
- assert.Contains(t, err.Error(), "mapping values are not allowed in this context: Line 2, its neighbor lines, or the lines on the same level")
+ assert.Contains(t, err.Error(), "line 2: mapping values are not allowed in this context")
}
func TestMissingRootNodeDeploymentYaml(t *testing.T) {
@@ -91,7 +91,7 @@ func TestMissingRootNodeDeploymentYaml(t *testing.T) {
_, err = p.ParseDeployment(tmpfile.Name())
assert.NotNil(t, err)
// go-yaml/yaml prints the wrong line number for mapping values. It should be 3.
- assert.Contains(t, err.Error(), "field name not found in struct parsers.YAML: Line 1, its neighbor lines, or the lines on the same level")
+ assert.Contains(t, err.Error(), "line 1: field name not found in struct parsers.YAML")
}
func TestParseDeploymentYAML_Application(t *testing.T) {
diff --git a/parsers/manifest_parser.go b/parsers/manifest_parser.go
index 5ead4b3..08c5c99 100644
--- a/parsers/manifest_parser.go
+++ b/parsers/manifest_parser.go
@@ -344,24 +344,24 @@ func (dm *YAMLParser) ComposeSequences(namespace string, sequences map[string]Se
func (dm *YAMLParser) ComposeActionsFromAllPackages(manifest *YAML, filePath string) ([]utils.ActionRecord, error) {
var s1 []utils.ActionRecord = make([]utils.ActionRecord, 0)
- manifestPackages := make(map[string]Package)
+ manifestPackages := make(map[string]Package)
if manifest.Package.Packagename != "" {
return dm.ComposeActions(filePath, manifest.Package.Actions, manifest.Package.Packagename)
} else {
- if manifest.Packages != nil {
- manifestPackages = manifest.Packages
- } else {
- manifestPackages = manifest.Application.Packages
- }
- }
- for n, p := range manifestPackages {
- a, err := dm.ComposeActions(filePath, p.Actions, n)
- if err == nil {
- s1 = append(s1, a...)
- } else {
- return nil, err
- }
- }
+ if manifest.Packages != nil {
+ manifestPackages = manifest.Packages
+ } else {
+ manifestPackages = manifest.Application.Packages
+ }
+ }
+ for n, p := range manifestPackages {
+ a, err := dm.ComposeActions(filePath, p.Actions, n)
+ if err == nil {
+ s1 = append(s1, a...)
+ } else {
+ return nil, err
+ }
+ }
return s1, nil
}
@@ -372,6 +372,10 @@ func (dm *YAMLParser) ComposeActions(filePath string, actions map[string]Action,
for key, action := range actions {
splitFilePath := strings.Split(filePath, string(os.PathSeparator))
+
+ // set the name of the action (which is the key)
+ action.Name = key
+
//set action.Function to action.Location
//because Location is deprecated in Action entity
if action.Function == "" && action.Location != "" {
@@ -430,7 +434,7 @@ func (dm *YAMLParser) ComposeActions(filePath string, actions map[string]Action,
code = base64.StdEncoding.EncodeToString([]byte(dat))
}
if ext == ".zip" && action.Runtime == "" {
- utils.PrintOpenWhiskOutputln("need explicit action Runtime value")
+ utils.PrintOpenWhiskOutputln("need explicit action Runtime value")
}
wskaction.Exec.Code = &code
}
@@ -442,12 +446,12 @@ func (dm *YAMLParser) ComposeActions(filePath string, actions map[string]Action,
wskaction.Exec.Kind = action.Runtime
} else if utils.Flags.Strict {
- wskaction.Exec.Kind = action.Runtime
- } else {
- errStr := wski18n.T("wskdeploy has chosen a particular runtime for the action.\n")
- whisk.Debug(whisk.DbgWarn, errStr)
- }
- }
+ wskaction.Exec.Kind = action.Runtime
+ } else {
+ errStr := wski18n.T("wskdeploy has chosen a particular runtime for the action.\n")
+ whisk.Debug(whisk.DbgWarn, errStr)
+ }
+ }
// we can specify the name of the action entry point using main
if action.Main != "" {
@@ -469,6 +473,7 @@ func (dm *YAMLParser) ComposeActions(filePath string, actions map[string]Action,
keyValArr = append(keyValArr, keyVal)
}
}
+
if len(keyValArr) > 0 {
wskaction.Parameters = keyValArr
}
@@ -484,6 +489,7 @@ func (dm *YAMLParser) ComposeActions(filePath string, actions map[string]Action,
// 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...
//wskaction.Annotations = keyValArr
wskaction.Annotations, errorParser = utils.WebAction("yes", keyValArr, action.Name, false)
if errorParser != nil {
@@ -687,7 +693,7 @@ func (dm *YAMLParser) ComposeApiRecords(pkg Package) ([]*whisk.ApiCreateRequest,
}
// TODO(): Support other valid Package Manifest types
-// TODO(): i.e., json (valid), timestamp, version, string256, string64, string16
+// TODO(): i.e., timestamp, version, string256, string64, string16
// TODO(): Support JSON schema validation for type: json
// TODO(): Support OpenAPI schema validation
@@ -702,6 +708,8 @@ var validParameterNameMap = map[string]string{
"int64": "integer",
"float32": "float",
"float64": "float",
+ "json": "json",
+ "map": "json",
}
var typeDefaultValueMap = map[string]interface{}{
@@ -709,6 +717,7 @@ var typeDefaultValueMap = map[string]interface{}{
"integer": 0,
"float": 0.0,
"boolean": false,
+ "json": make(map[string]interface{}),
// TODO() Support these types + their validation
// timestamp
// null
@@ -716,7 +725,6 @@ var typeDefaultValueMap = map[string]interface{}{
// string256
// string64
// string16
- // json
// scalar-unit
// schema
// object
@@ -738,8 +746,8 @@ func getTypeDefaultValue(typeName string) interface{} {
return nil
}
-func ResolveParamTypeFromValue(value interface{}, filePath string) (string, error) {
- // Note: string is the default type if not specified.
+func ResolveParamTypeFromValue(name string, value interface{}, filePath string) (string, error) {
+ // Note: 'string' is the default type if not specified and not resolvable.
var paramType string = "string"
var err error = nil
@@ -752,19 +760,11 @@ func ResolveParamTypeFromValue(value interface{}, filePath string) (string, erro
paramType = normalizedTypeName
} else {
- // raise an error if param is not a known type
- // TODO(): We have information to display to user on an error or warning here
- // TODO(): specifically, we have the parameter name, its value to show on error/warning
- // TODO(): perhaps this is a different Class of error? e.g., ErrorParameterMismatchError
- lines := []string{"Line Unknown"}
- msgs := []string{"Parameter value is not a known type. [" + actualType + "]"}
- err = utils.NewParserErr(filePath, lines, msgs)
+ // raise an error if parameter's value is not a known type
+ // TODO() - move string to i18n
+ msgs := []string{"Parameter [" + name + "] has a value that is not a known type. [" + actualType + "]"}
+ err = utils.NewParserErr(filePath, nil, msgs)
}
- } else {
-
- // TODO: The value may be supplied later, we need to support non-fatal warnings
- // raise an error if param is nil
- //err = utils.NewParserErr("",-1,"Paramter value is nil.")
}
return paramType, err
}
@@ -785,7 +785,7 @@ func ResolveParameter(paramName string, param *Parameter, filePath string) (inte
if !param.multiline {
// we have a single-line parameter declaration
// We need to identify parameter Type here for later validation
- param.Type, errorParser = ResolveParamTypeFromValue(param.Value, filePath)
+ 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.
@@ -811,13 +811,13 @@ func ResolveParameter(paramName string, param *Parameter, filePath string) (inte
// 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(param.Value, filePath)
+ 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) {
- lines := []string{"Line Unknown"}
- msgs := []string{"Invalid Type for parameter. [" + param.Type + "]"}
- return value, utils.NewParserErr(filePath, lines, msgs)
+ // 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
}
@@ -826,19 +826,30 @@ func ResolveParameter(paramName string, param *Parameter, filePath string) (inte
// Make sure the parameter's value is a valid, non-empty string and startsWith '$" (dollar) sign
value = utils.GetEnvVar(param.Value)
- typ := param.Type
+ // JSON - Handle both cases, where value 1) is a string containing JSON, 2) is a map of JSON
- // TODO(Priti): need to validate type is one of the supported primitive types with unit testing
- // TODO(): with the new logic, when would the following Unmarhsall() call be used?
- // if value is of type 'string' and its not empty <OR> if type is not 'string'
- if str, ok := value.(string); ok && (len(typ) == 0 || typ != "string") {
+ // 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
}
}
+ // 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
+ }
+ }
+
// Default to an empty string, do NOT error/terminate as Value may be provided later bu a Deployment file.
if value == nil {
value = getTypeDefaultValue(param.Type)
@@ -847,7 +858,7 @@ func ResolveParameter(paramName string, param *Parameter, filePath string) (inte
// Trace Parameter struct after resolution
//dumpParameter(paramName, param, "AFTER")
- //fmt.Printf("EXIT: value=[%v]\n", value)
+ //fmt.Printf("EXIT: Parameter type=[%v] value=[%v]\n", param.Type, value)
return value, errorParser
}
@@ -899,7 +910,7 @@ func dumpParameter(paramName string, param *Parameter, separator string) {
fmt.Printf("%s:\n", separator)
fmt.Printf("\t%s: (%T)\n", paramName, param)
if param != nil {
- fmt.Printf("\t\tParameter.Descrption: [%s]\n", param.Description)
+ fmt.Printf("\t\tParameter.Description: [%s]\n", param.Description)
fmt.Printf("\t\tParameter.Type: [%s]\n", param.Type)
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 cd1038c..39c1a6a 100644
--- a/parsers/manifest_parser_test.go
+++ b/parsers/manifest_parser_test.go
@@ -949,6 +949,83 @@ func TestResolveParameterForMultiLineParams(t *testing.T) {
}
+// Test 17: validate JSON parameters
+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)
+
+ // validate package name should be "validate"
+ packageName := "validate_json"
+ actionName := "validate_json_params"
+ expectedActionsCount := 1
+
+ assert.NotNil(t, m.Packages[packageName],
+ "Expected package named "+ packageName + " but got none")
+
+ // validate this package contains one action
+ actualActionsCount := len(m.Packages[packageName].Actions)
+ assert.Equal(t, expectedActionsCount, actualActionsCount,
+ "Expected " + string(expectedActionsCount) + " but got " + string(actualActionsCount))
+
+ if action, ok := m.Packages[packageName].Actions[actionName]; ok {
+ // validate location/function of an action to be "actions/dump_params.js"
+ expectedResult := "actions/dump_params.js"
+ actualResult := action.Function
+ assert.Equal(t, expectedResult, actualResult, "Expected action function " + expectedResult + " but got " + actualResult)
+
+ // validate runtime of an action to be "nodejs:6"
+ expectedResult = "nodejs:6"
+ actualResult = action.Runtime
+ assert.Equal(t, expectedResult, actualResult, "Expected action runtime " + expectedResult + " but got " + actualResult)
+
+ // validate the number of inputs to this action
+ expectedResult = strconv.FormatInt(6, 10)
+ actualResult = strconv.FormatInt(int64(len(action.Inputs)), 10)
+ assert.Equal(t, expectedResult, actualResult, "Expected " + expectedResult + " but got " + actualResult)
+
+ // validate inputs to this action
+ for input, param := range action.Inputs {
+ // Trace to help debug complex values:
+ // utils.PrintTypeInfo(input, param.Value)
+ switch input {
+ case "member1":
+ actualResult1 := param.Value.(string)
+ expectedResult1 := "{ \"name\": \"Sam\", \"place\": \"Shire\" }"
+ assert.Equal(t, expectedResult1, actualResult1, "Expected " + expectedResult + " but got " + actualResult)
+ case "member2":
+ actualResult2 := param.Value.(map[interface{}]interface{})
+ expectedResult2 := map[interface{}]interface{}{"name": "Sam", "place": "Shire"}
+ assert.Equal(t, expectedResult2, actualResult2, "Expected " + expectedResult + " but got " + actualResult)
+ case "member3":
+ actualResult3 := param.Value.(map[interface{}]interface{})
+ expectedResult3 := map[interface{}]interface{}{"name": "Elrond", "place": "Rivendell"}
+ assert.Equal(t, expectedResult3, actualResult3, "Expected " + expectedResult + " but got " + actualResult)
+ case "member4":
+ actualResult4 := param.Value.(map[interface{}]interface{})
+ expectedResult4 := map[interface{}]interface{}{"name": "Gimli", "place": "Gondor", "age": 139, "children": map[interface{}]interface{}{ "<none>": "<none>" }}
+ assert.Equal(t, expectedResult4, actualResult4, "Expected " + expectedResult + " but got " + actualResult)
+ case "member5":
+ actualResult5 := param.Value.(map[interface{}]interface{})
+ expectedResult5 := map[interface{}]interface{}{"name": "Gloin", "place": "Gondor", "age": 235, "children": map[interface{}]interface{}{ "Gimli": "Son" }}
+ assert.Equal(t, expectedResult5, actualResult5, "Expected " + expectedResult + " but got " + actualResult)
+ case "member6":
+ actualResult6 := param.Value.(map[interface{}]interface{})
+ expectedResult6 := map[interface{}]interface{}{"name": "Frodo", "place": "Undying Lands", "items": []interface{}{"Sting", "Mithril mail"}}
+ assert.Equal(t, expectedResult6, actualResult6, "Expected " + expectedResult + " but got " + actualResult)
+ }
+ }
+
+ // 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{})
+ //}
+ }
+}
+
func _createTmpfile(data string, filename string) (f *os.File, err error) {
dir, _ := os.Getwd()
tmpfile, err := ioutil.TempFile(dir, filename)
@@ -1256,7 +1333,7 @@ func TestInvalidKeyManifestYaml(t *testing.T) {
_, err = p.ParseManifest(tmpfile.Name())
assert.NotNil(t, err)
// go-yaml/yaml prints the wrong line number for mapping values. It should be 4.
- assert.Contains(t, err.Error(), "field invalidKey not found in struct parsers.Package: Line 2, its neighbor lines, or the lines on the same level")
+ assert.Contains(t, err.Error(), "line 2: field invalidKey not found in struct parsers.Package")
}
func TestMappingValueManifestYaml(t *testing.T) {
@@ -1277,7 +1354,7 @@ func TestMappingValueManifestYaml(t *testing.T) {
_, err = p.ParseManifest(tmpfile.Name())
assert.NotNil(t, err)
// go-yaml/yaml prints the wrong line number for mapping values. It should be 5.
- assert.Contains(t, err.Error(), "mapping values are not allowed in this context: Line 4, its neighbor lines, or the lines on the same level")
+ assert.Contains(t, err.Error(), "line 4: mapping values are not allowed in this context")
}
func TestMissingRootValueManifestYaml(t *testing.T) {
@@ -1295,7 +1372,7 @@ func TestMissingRootValueManifestYaml(t *testing.T) {
p := NewYAMLParser()
_, err = p.ParseManifest(tmpfile.Name())
assert.NotNil(t, err)
- assert.Contains(t, err.Error(), "field actions not found in struct parsers.YAML: Line 1, its neighbor lines, or the lines on the same level")
+ assert.Contains(t, err.Error(), "line 1: field actions not found in struct parsers.YAML")
}
diff --git a/parsers/yamlparser.go b/parsers/yamlparser.go
index 944f624..4b7a2db 100644
--- a/parsers/yamlparser.go
+++ b/parsers/yamlparser.go
@@ -105,15 +105,15 @@ type Parameter struct {
type Trigger struct {
//mapping to ????
- Feed string `yaml:"feed"` //used in manifest.yaml
+ Feed string `yaml:"feed"` //used in manifest.yaml
//mapping to wsk.Trigger.Namespace
Namespace string `yaml:"namespace"` //used in deployment.yaml
Credential string `yaml:"credential"` //used in deployment.yaml
Inputs map[string]Parameter `yaml:"inputs"` //used in deployment.yaml
//mapping to wsk.Trigger.Name
- Name string
+ Name string
Annotations map[string]interface{} `yaml:"annotations,omitempty"`
- Source string `yaml:source` // deprecated, used in manifest.yaml
+ Source string `yaml:source` // deprecated, used in manifest.yaml
//Parameters map[string]interface{} `yaml:parameters` // used in manifest.yaml
}
diff --git a/tests/dat/manifest_bad_yaml_2.yaml b/tests/dat/manifest_bad_yaml_2.yaml
new file mode 100644
index 0000000..bd4d895
--- /dev/null
+++ b/tests/dat/manifest_bad_yaml_2.yaml
@@ -0,0 +1,8 @@
+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
new file mode 100644
index 0000000..bcffb6e
--- /dev/null
+++ b/tests/dat/manifest_bad_yaml_3.yaml
@@ -0,0 +1,8 @@
+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
new file mode 100644
index 0000000..da73f3e
--- /dev/null
+++ b/tests/dat/manifest_bad_yaml_4.yaml
@@ -0,0 +1,6 @@
+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_validate_json_params.yaml b/tests/dat/manifest_validate_json_params.yaml
new file mode 100644
index 0000000..dd773a0
--- /dev/null
+++ b/tests/dat/manifest_validate_json_params.yaml
@@ -0,0 +1,33 @@
+packages:
+ validate_json:
+ actions:
+ validate_json_params:
+ function: actions/dump_params.js
+ runtime: nodejs:6
+ inputs:
+ member1:
+ type: json
+ value: '{ "name": "Sam", "place": "Shire" }'
+ member2: { "name": "Sam", "place": "Shire" }
+ member3:
+ type: json
+ value: { "name": "Elrond", "place": "Rivendell" }
+ member4:
+ type: json
+ value: { "name": "Gimli", "place": "Gondor", "age": 139, "children": { "<none>": "<none>" } }
+ member5:
+ type: json
+ value: {
+ "name": "Gloin",
+ "place": "Gondor",
+ "age": 235,
+ "children": {
+ "Gimli": "Son"
+ }
+ }
+ member6:
+ type: json
+ value: { "name": "Frodo", "place": "Undying Lands", "items": [ "Sting", "Mithril mail" ] }
+ outputs:
+ fellowship:
+ type: json
diff --git a/utils/conversion.go b/utils/conversion.go
new file mode 100644
index 0000000..411cf9c
--- /dev/null
+++ b/utils/conversion.go
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package utils
+
+import "fmt"
+
+func convertInterfaceArray(in []interface{}) []interface{} {
+ res := make([]interface{}, len(in))
+ for i, v := range in {
+ res[i] = convertMapValue(v)
+ }
+ return res
+}
+
+func ConvertInterfaceMap(mapIn map[interface{}]interface{}) map[string]interface{} {
+ mapOut := make(map[string]interface{})
+ for k, v := range mapIn {
+ mapOut[fmt.Sprintf("%v", k)] = convertMapValue(v)
+ }
+ return mapOut
+}
+
+func convertMapValue(value interface{}) interface{} {
+ switch typedVal := value.(type) {
+ case []interface{}:
+ return convertInterfaceArray(typedVal)
+ case map[interface{}]interface{}:
+ return ConvertInterfaceMap(typedVal)
+ case string:
+ return typedVal
+ default:
+ return fmt.Sprintf("%v", typedVal)
+ }
+}
+
+func PrintTypeInfo(name string, value interface{}) {
+ info := fmt.Sprintf("Name=[%s], Value=[%v], Type=[%T]\n",name,value,value)
+ fmt.Print(info)
+}
diff --git a/utils/dependencies.go b/utils/dependencies.go
index ff569dc..5ece712 100644
--- a/utils/dependencies.go
+++ b/utils/dependencies.go
@@ -79,4 +79,3 @@ func LocationIsGithub(location string) bool {
return false
}
-
diff --git a/utils/misc.go b/utils/misc.go
index 481407c..5775872 100644
--- a/utils/misc.go
+++ b/utils/misc.go
@@ -100,8 +100,8 @@ func PrettyJSON(j interface{}) (string, error) {
formatter := prettyjson.NewFormatter()
bytes, err := formatter.Marshal(j)
if err != nil {
- return "", err
- }
+ return "", err
+ }
return string(bytes), nil
}
@@ -670,16 +670,15 @@ func (urlReader *URLReader) ReadUrl(url string) (content []byte, err error) {
type LocalReader struct {
}
-func (localReader *LocalReader) ReadLocal(path string) (content []byte, err error) {
+func (localReader *LocalReader) ReadLocal(path string) ([]byte, error) {
cont, err := ioutil.ReadFile(path)
return cont, err
}
-func Read(url string) (content []byte, err error) {
+func Read(url string) ([]byte, error) {
if strings.HasPrefix(url, "http") {
return new(ContentReader).URLReader.ReadUrl(url)
} else {
return new(ContentReader).LocalReader.ReadLocal(url)
}
}
-
diff --git a/utils/wskdeployerror.go b/utils/wskdeployerror.go
index 3037434..d699184 100644
--- a/utils/wskdeployerror.go
+++ b/utils/wskdeployerror.go
@@ -22,16 +22,17 @@ import (
"runtime"
"github.com/apache/incubator-openwhisk-wskdeploy/wski18n"
"strings"
+ "path/filepath"
)
const (
INVALID_YAML_INPUT = "Invalid input of Yaml file"
INVALID_YAML_FORMAT = "Invalid input of Yaml format"
OPENWHISK_CLIENT_ERROR = "OpenWhisk Client Error"
- MANIFEST_NOT_FOUND = INVALID_YAML_INPUT
+ MANIFEST_NOT_FOUND = INVALID_YAML_INPUT // TODO{} This should be a unique message.
UNKNOWN = "Unknown"
+ UNKNOWN_VALUE = "Unknown value"
LINE = "line"
- PARSING_ERR = "YAML: Parsing errors:"
)
type TestCaseError struct {
@@ -80,7 +81,8 @@ func NewErrorManifestFileNotFound(errMessage string) *ErrorManifestFileNotFound
var err = &ErrorManifestFileNotFound{
errorType: wski18n.T(MANIFEST_NOT_FOUND),
}
- err.SetFileName(fn)
+ //err.SetFileName(fn)
+ err.SetFileName(filepath.Base(fn))
err.SetLineNum(lineNum)
err.SetMessage(errMessage)
return err
@@ -100,7 +102,7 @@ func NewInputYamlFileError(errMessage string) *InputYamlFileError {
var err = &InputYamlFileError{
errorType: wski18n.T(INVALID_YAML_INPUT),
}
- err.SetFileName(fn)
+ err.SetFileName(filepath.Base(fn))
err.SetLineNum(lineNum)
err.SetMessage(errMessage)
return err
@@ -111,7 +113,7 @@ func (e *InputYamlFileError) SetErrorType(errorType string) {
}
func (e *InputYamlFileError) Error() string {
- return fmt.Sprintf("%s [%d]: %s =====> %s\n", e.FileName, e.LineNum, e.errorType, e.Message)
+ return fmt.Sprintf("%s [%d]: %s %s\n", e.FileName, e.LineNum, e.errorType, e.Message)
}
type InputYamlFormatError struct {
@@ -184,14 +186,16 @@ func NewParserErr(yamlFile string, lines []string, msgs []string) *ParserErr {
func (e *ParserErr) Error() string {
result := make([]string, len(e.msgs))
- for index, each := range e.msgs {
+ var fn = filepath.Base(e.FileName)
+
+ for index, msg := range e.msgs {
var s string
- if e.lines[index] == UNKNOWN {
- s = fmt.Sprintf("%s", PARSING_ERR)
- } else {
- s = fmt.Sprintf("%s: Line %s, its neighbor lines, or the lines on the same level.", each, e.lines[index])
+ if e.lines == nil || e.lines[index] == UNKNOWN {
+ s = fmt.Sprintf("====> %s", msg)
+ } else{
+ s = fmt.Sprintf("====> Line [%v]: %s", e.lines[index], msg)
}
result[index] = s
}
- return fmt.Sprintf("%s [%d]:\n Failed to parse the yaml file %s\n =====> %s\n", e.FileName, e.LineNum, e.YamlFile, strings.Join(result, "\n "))
+ return fmt.Sprintf("\n==> %s [%d]: Failed to parse the yaml file: %s: \n%s", fn, e.LineNum, e.YamlFile, strings.Join(result, "\n"))
}
--
To stop receiving notification emails like this one, please contact
['"commits@openwhisk.apache.org" <co...@openwhisk.apache.org>'].