Build a Serverless app using Go and Azure Functions

By Abhishek Gupta

func main() { port, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT") if !exists { port = "8080" } http.HandleFunc("/api/funcy", function.Funcy) log.Fatal(http.ListenAndServe(":"+port, nil))

}

All the heavy lifting is done in function/function.go.

The first part is to read the request body (from Slack) and ensure its integrity via a signature validation process based on this recipe defined by Slack.

signingSecret := os.Getenv("SLACK_SIGNING_SECRET")
apiKey := os.Getenv("GIPHY_API_KEY")
if signingSecret == "" || apiKey == "" { http.Error(w, "Failed to process request. Please contact the admin", http.StatusUnauthorized) return

}

slackTimestamp := r.Header.Get("X-Slack-Request-Timestamp")
b, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, "Failed to process request", http.StatusBadRequest) return } slackSigningBaseString := "v0:" + slackTimestamp + ":" + string(b)

slackSignature := r.Header.Get("X-Slack-Signature")

if !matchSignature(slackSignature, signingSecret, slackSigningBaseString) { http.Error(w, "Function was not invoked by Slack", http.StatusForbidden) return

}

Once we’ve confirmed that the function has indeed being invoked via Slack, the next part is to extract the search term entered by the (Slack) user

vals, err := parse(b) if err != nil { http.Error(w, "Failed to process request", http.StatusBadRequest) return }

giphyTag := vals.Get("text")

Look up for GIFs with the search term by invoking the GIPHY REST API

giphyResp, err := http.Get("http://api.giphy.com/v1/gifs/random?tag=" + giphyTag + "&api_key=" + apiKey) if err != nil { http.Error(w, "Failed to process request", http.StatusFailedDependency) return

}

resp, err := ioutil.ReadAll(giphyResp.Body) if err != nil { http.Error(w, "Failed to process request", http.StatusInternalServerError) return

}

Un-marshal the response sent back by the GIPHY API, convert it into a form which Slack can make sense of and return it. That’s it !

var gr GiphyResponsejson.Unmarshal(resp, &gr)title := gr.Data.Title

url := gr.Data.Images.Downsized.URL

slackResponse := SlackResponse{Text: slackResponseStaticText, Attachments: []Attachment{{Text: title, ImageURL: url}}}
w.Header().Set("Content-Type", "application/json")json.NewEncoder(w).Encode(slackResponse)

fmt.Println("Sent response to Slack")

Check the matchSignature function if you're interested in checking the signature validation process and look at slack.go, giphy.go (in the function directory) to see the Go structs used represent information (JSON) being exchanged between various components. These have not been included here to keep this post concise.

Alright! So far, we have covered lots of theory and background info. It’s time to get things done! Before you proceed, ensure that you take care of the below mentioned pre-requisites.