Treat inline parameter value as JSON and add support for expanded par… (#221)
* Treat inline parameter value as JSON and add support for expanded parameters (#219)
* Must resolve parameters for actions and triggers as well (#219)
* Remove unused import. (#219)
diff --git a/deployers/servicedeployer.go b/deployers/servicedeployer.go
index ef32659..af68e11 100644
--- a/deployers/servicedeployer.go
+++ b/deployers/servicedeployer.go
@@ -650,23 +650,14 @@
fmt.Println("Name: " + pack.Package.Name)
fmt.Println(" bindings: ")
for _, p := range pack.Package.Parameters {
- value := "?"
- if str, ok := p.Value.(string); ok {
- value = str
- }
- fmt.Println(" - name: " + p.Key + " value: " + value)
+ fmt.Printf(" - %s : %v\n", p.Key, utils.PrettyJSON(p.Value))
}
for _, action := range pack.Actions {
fmt.Println(" * action: " + action.Action.Name)
fmt.Println(" bindings: ")
for _, p := range action.Action.Parameters {
-
- value := "?"
- if str, ok := p.Value.(string); ok {
- value = str
- }
- fmt.Println(" - name: " + p.Key + " value: " + value)
+ fmt.Printf(" - %s : %v\n", p.Key, utils.PrettyJSON(p.Value))
}
fmt.Println(" annotations: ")
for _, p := range action.Action.Annotations {
@@ -689,12 +680,7 @@
fmt.Println(" bindings: ")
for _, p := range trigger.Parameters {
-
- value := "?"
- if str, ok := p.Value.(string); ok {
- value = str
- }
- fmt.Println(" - name: " + p.Key + " value: " + value)
+ fmt.Printf(" - %s : %v\n", p.Key, utils.PrettyJSON(p.Value))
}
fmt.Println(" annotations: ")
diff --git a/parsers/manifest_parser.go b/parsers/manifest_parser.go
index a90776f..4482770 100644
--- a/parsers/manifest_parser.go
+++ b/parsers/manifest_parser.go
@@ -24,6 +24,7 @@
"path"
"strings"
+ "encoding/json"
"github.com/openwhisk/openwhisk-client-go/whisk"
"github.com/openwhisk/openwhisk-wskdeploy/utils"
"gopkg.in/yaml.v2"
@@ -95,12 +96,15 @@
pag.Publish = &pub
keyValArr := make(whisk.KeyValueArr, 0)
- for name, value := range mani.Package.Inputs {
+ for name, param := range mani.Package.Inputs {
var keyVal whisk.KeyValue
keyVal.Key = name
- keyVal.Value = utils.GetEnvVar(value)
- keyValArr = append(keyValArr, keyVal)
+ keyVal.Value = ResolveParameter(¶m)
+
+ if keyVal.Value != nil {
+ keyValArr = append(keyValArr, keyVal)
+ }
}
if len(keyValArr) > 0 {
@@ -178,12 +182,15 @@
}
keyValArr := make(whisk.KeyValueArr, 0)
- for name, value := range action.Inputs {
+ for name, param := range action.Inputs {
var keyVal whisk.KeyValue
keyVal.Key = name
- keyVal.Value = utils.GetEnvVar(value)
- keyValArr = append(keyValArr, keyVal)
+ keyVal.Value = ResolveParameter(¶m)
+
+ if keyVal.Value != nil {
+ keyValArr = append(keyValArr, keyVal)
+ }
}
if len(keyValArr) > 0 {
@@ -239,12 +246,15 @@
}
keyValArr = make(whisk.KeyValueArr, 0)
- for name, value := range trigger.Inputs {
+ for name, param := range trigger.Inputs {
var keyVal whisk.KeyValue
keyVal.Key = name
- keyVal.Value = utils.GetEnvVar(value)
- keyValArr = append(keyValArr, keyVal)
+ keyVal.Value = ResolveParameter(¶m)
+
+ if keyVal.Value != nil {
+ keyValArr = append(keyValArr, keyVal)
+ }
}
if len(keyValArr) > 0 {
@@ -276,3 +286,54 @@
wskaction.Namespace = action.Namespace
return wskaction, err
}
+
+// Resolve parameter input
+func ResolveParameter(param *Parameter) interface{} {
+ value := utils.GetEnvVar(param.Value)
+
+ typ := param.Type
+ if str, ok := value.(string); ok && (len(typ) == 0 || typ != "string") {
+ var parsed interface{}
+ err := json.Unmarshal([]byte(str), &parsed)
+ if err == nil {
+ return parsed
+ }
+ }
+ return value
+}
+
+// Provide custom Parameter marshalling and unmarshalling
+
+type ParsedParameter Parameter
+
+func (n *Parameter) UnmarshalYAML(unmarshal func(interface{}) error) error {
+ var aux ParsedParameter
+ if err := unmarshal(&aux); err == nil {
+ n.Type = aux.Type
+ n.Description = aux.Description
+ n.Value = aux.Value
+ n.Required = aux.Required
+ n.Default = aux.Default
+ n.Status = aux.Status
+ n.Schema = aux.Schema
+ return nil
+ }
+
+ var inline interface{}
+ if err := unmarshal(&inline); err != nil {
+ return err
+ }
+
+ n.Value = inline
+ return nil
+}
+
+func (n *Parameter) MarshalYAML() (interface{}, error) {
+ if _, ok := n.Value.(string); len(n.Type) == 0 && len(n.Description) == 0 && ok {
+ if !n.Required && len(n.Status) == 0 && n.Schema == nil {
+ return n.Value.(string), nil
+ }
+ }
+
+ return n, nil
+}
diff --git a/parsers/yamlparser.go b/parsers/yamlparser.go
index dcf6c86..09dae33 100644
--- a/parsers/yamlparser.go
+++ b/parsers/yamlparser.go
@@ -56,7 +56,7 @@
//mapping to wsk.Action.Namespace
Namespace string `yaml:"namespace"` //used in deployment.yaml
Credential string `yaml:"credential"` //used in deployment.yaml
- Inputs map[string]interface{} `yaml:"inputs"` //used in both manifest.yaml and 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
//mapping to wsk.Action.Name
Name string
@@ -75,13 +75,23 @@
Version string
}
+type Parameter struct {
+ Type string `yaml:"type,omitempty"`
+ Description string `yaml:"description,omitempty"`
+ Value interface{} `yaml:"value,omitempty"` // JSON Value
+ Required bool `yaml:"required,omitempty"`
+ Default interface{} `yaml:"default,omitempty"`
+ Status string `yaml:"status,omitempty"`
+ Schema interface{} `yaml:"schema,omitempty"`
+}
+
type Trigger struct {
//mapping to ????
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]interface{} `yaml:"inputs"` //used in deployment.yaml
+ 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
Annotations map[string]interface{} `yaml:"annotations,omitempty"`
@@ -133,7 +143,7 @@
Triggers map[string]Trigger `yaml:"triggers"` //used in both manifest.yaml and deployment.yaml
Feeds map[string]Feed `yaml:"feeds"` //used in both manifest.yaml and deployment.yaml
Rules map[string]Rule `yaml:"rules"` //used in both manifest.yaml and deployment.yaml
- Inputs map[string]interface{} `yaml:"inputs"` //used in deployment.yaml
+ Inputs map[string]Parameter `yaml:"inputs"` //used in deployment.yaml
Sequences map[string]Sequence `yaml:"sequences"`
Annotations map[string]interface{} `yaml:"annotations,omitempty"`
//Parameters map[string]interface{} `yaml: parameters` // used in manifest.yaml
diff --git a/specification/openwhisk_v0.8.3.pdf b/specification/openwhisk_v0.8.3.pdf
index 5007882..85466a3 100644
--- a/specification/openwhisk_v0.8.3.pdf
+++ b/specification/openwhisk_v0.8.3.pdf
Binary files differ
diff --git a/tests/dat/manifest6.yaml b/tests/dat/manifest6.yaml
new file mode 100644
index 0000000..6fa6f2e
--- /dev/null
+++ b/tests/dat/manifest6.yaml
@@ -0,0 +1,45 @@
+package:
+ name: manifest6
+ actions:
+ action1:
+ inputs:
+ inline1: '{ "key": true }'
+ inline2: Just a string
+ inline3: null
+ inline4: true
+ inline5: 42
+ inline6: -531
+ inline7: 432.432E-43
+ inline8: '[ true, null, "boo", { "key": 0 }]'
+ inline9: !!bool false
+ inline0: !!float 456.423
+ inlin10: # JSON null
+ inlin11: True # JSON true
+
+ expand1:
+ value: null
+ type: string
+
+ expand2:
+ value: true
+ type: string
+
+ expand3:
+ value: false
+ type: string
+
+ expand4:
+ value: 15646
+ type: string
+
+ expand5:
+ value: '{ "key": true }'
+ type: string
+
+ expand6:
+ value: '[ true, null, "boo", { "key": 0 }]'
+ type: string
+
+ expand7:
+ value: !!null null
+ type: string
\ No newline at end of file
diff --git a/tests/src/deployers/manifestreader_test.go b/tests/src/deployers/manifestreader_test.go
index 64b85a2..7945327 100644
--- a/tests/src/deployers/manifestreader_test.go
+++ b/tests/src/deployers/manifestreader_test.go
@@ -12,6 +12,7 @@
var ms *parsers.ManifestYAML
func init() {
+
sd = deployers.NewServiceDeployer()
sd.ManifestPath = manifest_file
mr = deployers.NewManfiestReader(sd)
@@ -30,3 +31,12 @@
err := mr.InitRootPackage(ps, ms)
assert.Equal(t, err, nil, "Init Root Package failed")
}
+
+// Test Parameters
+func TestManifestReader_param(t *testing.T) {
+ ms := ps.ParseManifest("../../dat/manifest6.yaml")
+ err := mr.InitRootPackage(ps, ms)
+ assert.Equal(t, err, nil, "Init Root Package failed")
+
+ // TODO.
+}
diff --git a/tests/src/parser/yamlparser_test.go b/tests/src/parser/yamlparser_test.go
index 3ff335d..5ba08f0 100644
--- a/tests/src/parser/yamlparser_test.go
+++ b/tests/src/parser/yamlparser_test.go
@@ -13,6 +13,7 @@
var manifestfile3 = "../../dat/manifest3.yaml"
var manifestfile4 = "../../dat/manifest4.yaml"
var manifestfile5 = "../../dat/manifest5.yaml"
+var manifestfile6 = "../../dat/manifest6.yaml"
var testfile1 = "../../dat/deploy1.yaml"
var testfile2 = "../../dat/deploy2.yaml"
var testfile3 = "../../dat/deploy3.yaml"
@@ -131,6 +132,74 @@
}
}
+func TestParseManifestYAML_param(t *testing.T) {
+ data, err := ioutil.ReadFile(manifestfile6)
+ if err != nil {
+ panic(err)
+ }
+
+ var manifest parsers.ManifestYAML
+ err = parsers.NewYAMLParser().Unmarshal(data, &manifest)
+ if err != nil {
+ panic(err)
+ }
+
+ assert.Equal(t, 1, len(manifest.Package.Actions), "Get action list failed.")
+ for action_name := range manifest.Package.Actions {
+ var action = manifest.Package.Actions[action_name]
+ switch action_name {
+ case "action1":
+ for param_name := range action.Inputs {
+ var param = action.Inputs[param_name]
+ switch param_name {
+ case "inline1":
+ assert.Equal(t, "{ \"key\": true }", param.Value, "Get param value failed.")
+ case "inline2":
+ assert.Equal(t, "Just a string", param.Value, "Get param value failed.")
+ case "inline3":
+ assert.Equal(t, nil, param.Value, "Get param value failed.")
+ case "inline4":
+ assert.Equal(t, true, param.Value, "Get param value failed.")
+ case "inline5":
+ assert.Equal(t, 42, param.Value, "Get param value failed.")
+ case "inline6":
+ assert.Equal(t, -531, param.Value, "Get param value failed.")
+ case "inline7":
+ assert.Equal(t, 432.432E-43, param.Value, "Get param value failed.")
+ case "inline8":
+ assert.Equal(t, "[ true, null, \"boo\", { \"key\": 0 }]", param.Value, "Get param value failed.")
+ case "inline9":
+ assert.Equal(t, false, param.Value, "Get param value failed.")
+ case "inline0":
+ assert.Equal(t, 456.423, param.Value, "Get param value failed.")
+ case "inlin10":
+ assert.Equal(t, nil, param.Value, "Get param value failed.")
+ case "inlin11":
+ assert.Equal(t, true, param.Value, "Get param value failed.")
+ case "expand1":
+ assert.Equal(t, nil, param.Value, "Get param value failed.")
+ case "expand2":
+ assert.Equal(t, true, param.Value, "Get param value failed.")
+ case "expand3":
+ assert.Equal(t, false, param.Value, "Get param value failed.")
+ case "expand4":
+ assert.Equal(t, 15646, param.Value, "Get param value failed.")
+ case "expand5":
+ assert.Equal(t, "{ \"key\": true }", param.Value, "Get param value failed.")
+ case "expand6":
+ assert.Equal(t, "[ true, null, \"boo\", { \"key\": 0 }]", param.Value, "Get param value failed.")
+ case "expand7":
+ assert.Equal(t, nil, param.Value, "Get param value failed.")
+ default:
+ t.Error("Get param name failed")
+ }
+ }
+ default:
+ t.Error("Get action name failed")
+ }
+ }
+}
+
func TestParseDeploymentYAML_Application(t *testing.T) {
//var deployment utils.DeploymentYAML
mm := parsers.NewYAMLParser()
@@ -158,8 +227,8 @@
assert.Equal(t, "12345678ABCDEF", pkg.Credential, "Get package credential failed.")
assert.Equal(t, 1, len(pkg.Inputs), "Get package input list failed.")
//get and verify inputs
- for param_name, value := range pkg.Inputs {
- assert.Equal(t, "value", value, "Get input value failed.")
+ for param_name, param := range pkg.Inputs {
+ assert.Equal(t, "value", param.Value, "Get input value failed.")
assert.Equal(t, "param", param_name, "Get input param name failed.")
}
}
@@ -179,11 +248,11 @@
assert.Equal(t, "12345678ABCDEF", action.Credential, "Get action credential failed.")
assert.Equal(t, 1, len(action.Inputs), "Get package input list failed.")
//get and verify inputs
- for param_name, value := range action.Inputs {
- switch value.(type) {
+ for param_name, param := range action.Inputs {
+ switch param.Value.(type) {
case string:
assert.Equal(t, "name", param_name, "Get input param name failed.")
- assert.Equal(t, "Bernie", value, "Get input value failed.")
+ assert.Equal(t, "Bernie", param.Value, "Get input value failed.")
default:
t.Error("Get input value type failed.")
}
diff --git a/utils/misc.go b/utils/misc.go
index 10a7c20..ebed895 100644
--- a/utils/misc.go
+++ b/utils/misc.go
@@ -25,6 +25,7 @@
"bufio"
+ "github.com/hokaccha/go-prettyjson"
"github.com/openwhisk/openwhisk-client-go/whisk"
"os"
"reflect"
@@ -98,6 +99,13 @@
}
+func PrettyJSON(j interface{}) string {
+ formatter := prettyjson.NewFormatter()
+ bytes, err := formatter.Marshal(j)
+ Check(err)
+ return string(bytes)
+}
+
// Common utilities
// Prompt for user input
@@ -127,3 +135,12 @@
}
return key
}
+
+var kindToJSON []string = []string{"", "boolean", "integer", "integer", "integer", "integer", "integer", "integer", "integer", "integer",
+ "integer", "integer", "integer", "number", "number", "number", "number", "array", "", "", "", "object", "", "", "string", "", ""}
+
+// Gets JSON type name
+func GetJSONType(j interface{}) string {
+ fmt.Print(reflect.TypeOf(j).Kind())
+ return kindToJSON[reflect.TypeOf(j).Kind()]
+}