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(&param)
+
+		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(&param)
+
+			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(&param)
+
+			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()]
+}