apache/openwhisk-runtime-go

Change function Main signature to match OpenWhisk base Conformance JSON IN<->OUT

csantanapr opened this issue · 5 comments

Currently the main function signature takes 1 input (raw []bytes) and returns 2 outputs (raw [] bytes, error)
I think is ok to have alternative signatures like we do for Swift and Codable, and 1 input, an 0 output.
This is done to adapt better with developer experience and the language idioms

But the base OW signature is the building block and the first one to be offer.

Currently the signature is

func Main(event json.RawMessage) (json.RawMessage, error)

In example:

// Main is the function implementing the action
func Main(event json.RawMessage) (json.RawMessage, error) {
  // decode the json
  var obj map[string]interface{}
  json.Unmarshal(event, &obj)
  // do your work
  name, ok := obj["name"].(string)
  if !ok {
    name = "Stranger"
  }
  msg := map[string]string{"message": ("Hello, " + name + "!")}
  // log in stdout or in stderr
  log.Printf("name=%s\n", name)
  // encode the result back in json
  return json.Marshal(msg)
}

This also has a drawback that dev will be 99% will be unmarshalling the input and the output.
in the way in

  var obj map[string]interface{}
  json.Unmarshal(event, &obj)

in the way out

json.Marshal(msg)

I propose we start with this base signature instead:

func Main(params map[string]interface{}) map[string]interface{} 

in example much simple for developer to deal with

func Main(params map[string]interface{}) map[string]interface{} {
	name, ok := params["name"].(string)
	if !ok {
		name = "World"
	}
	var result = make(map[string]interface{})
	result["body"] = "Hello " + name
	return result
}

to return error same base API for OW, return a error key in the out

...
myError["message"] = "My error msg here for OW to bubble up"
result["error"] = myError
return result

cc @sciabarra @rabbah

As secondary signature can be added to have a variation that return 2 results in output to be more golang friendly

func Main(params map[string]interface{}) (map[string]interface{}, error)

So proposal would be to the following signatures

func Main(params map[string]interface{}) map[string]interface{}
func Main(params map[string]interface{}) (map[string]interface{}, error)

As we get this out in to the main default deployment as a kind --kind golang:1.10
We could get feedback that other signatures to be added that include some sort of streams using golang channels.

Here is a reference to a web server action I used while experimenting with the new signature

package main
import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)
func parsedInput(r *http.Request) map[string]interface{} {
	body, _ := ioutil.ReadAll(r.Body)
	defer r.Body.Close()
	var payload map[string]interface{}
	json.Unmarshal(body, &payload)
	return payload["value"].(map[string]interface{})
}
func runHandler(w http.ResponseWriter, r *http.Request) {
	out, _ := json.Marshal(Main(parsedInput(r)))
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprint(w, string(out))
}
func initHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprint(w, `{"ok":true}`)
}
// Main my cool serverless function
func Main(params map[string]interface{}) map[string]interface{} {
	name, ok := params["name"].(string)
	if !ok {
		name = "World"
	}
	var result = make(map[string]interface{})
	result["body"] = "Hello " + name
	return result
}
func main() {
	http.HandleFunc("/init", initHandler)
	http.HandleFunc("/run", runHandler)
	http.ListenAndServe(":8080", nil)
}

Ok I want to check how this can be done. A simple way would be to use a regular expression to recognize the signature But I am trying to find better ways. Assign to me (tag it sciabarracom)

Changed the main signature please close this bug

This is fixed now