Go Template

This template allows you to code Functions using Golang 1.15. The skeleton of this template looks like this:
package function

import (
        "io/ioutil"
        "net/http"
)
func Handle(w http.ResponseWriter, r *http.Request) {
        var body []byte

        if r.body !=nil {
                    defer r.Body.Close()
                    body, _= ioutil.ReadAll(r.Body)
        }

        w.WriteHeader(http.StatusOK)
        w.Write(body)

This code echoes back your request body message with a 200 status code. Probably not very handy but a good starting point.

The Handle function is an HTTP Handler within which your task must run. You can use more functions in your code to make your code cleaner, but the Handle function is the entry point of your serverless function.

Handlers are part of the Go language, so there is really nothing new in this template. Just make sure you write in the http.ResponseWriter in the correct order! You can easily find tons of great information on how to use them. Here are few sources:

Libraries

Adding third-party dependencies is not allowed at the moment, although this will change in the future. For now, you can use the following pre-installed dependencies (this list will grow):

SWX Package

The function/SWX package allows some operations using the Altair IoT Studio API:
  • Get an access token.
    Note: This package is in a very early stage of development and only offers a very limited number of features.

Secrets

You can access your Secrets using the two built-in Functions in the function/secrets package:
func Get(secretName string) string
Returns the value of the given secret. If the secret is not found, an empty string will be returned.
func Exists(secretName string) bool
Returns a boolean value indicating if the given secret is found.
Example:
package function 
import ( 
        "function/secrets" 
        "net/http" 
) 
func Handle(w http.ResponseWriter, r *http.Request) { 
        // ... 
        if secrets.Exists("my_password") { 
            mySecret := secrets.Get("my_password") 
            // ... 
        } 
        // ... 
}

Logging

Anything you write to stderr will be saved as a log entry. You can use the log package for that:
package function

import (
        "log"
        "net/http"
)

func Handle(w http.ResponseWriter, r *http.Request) {
        // ...

        log.Println("My log entry!")
        
        // ...

}

Examples

Here are some simple examples to illustrate how you can write your own functions using Go.

Adder
This is a really simple function that gets a comma-separated list of numbers in the request body and return the sum of them (or an error if the input format is invalid).
package function

import (
        "fmt"
        "io/ioutil"
        "net/http"
        "strconv"
        "strings"
)

func Handle(w http.ResponseWriter, r *http.Request) {
        var body []byte
        var sum float64 = 0

        //Gets function input
        if r.Body != nil {
            defer r.Body.Close()
            body, _=ioutil.ReadAll(r.Body)
        }

        // Parses input as a comma-separated list of numbers
        numbers := strings.Split(string(body), ",")

        //Tries to convert all values to numbers
        for _, number :=range numbers {
            number = strings.TrimSpace(number)
            if value, err :=strconv.ParserFloat(number, 32); err != nil {
                    //Throws an error if something is not a number
                    w.WriteHeader(http.StatusBadRequest)
                    w.Write([]byte("Invalid input format!"))
                    return
            } else {
                    sum += value
            }
        }

        w.WriterHeader(http.StatusOK)
        w.Write([]byte(fmt.Sprintf("%f", sum)))
}
MQTT Publisher
The following example shows how to set up an MQTT client to publish the payload of the request to the functions/go topic.
package function

import (
	"fmt"
	mqtt "github.com/eclipse/paho.mqtt.golang"
	"io/ioutil"
	"net/http"
)

const (
	MQTTBroker = "test.mosquitto.org"
	MQTTPort   = 1883
)

func Handle(w http.ResponseWriter, r *http.Request) {
	var input []byte

	if r.Body != nil {
		defer r.Body.Close()
		input, _ = ioutil.ReadAll(r.Body)
	}

	opts := mqtt.NewClientOptions()
	opts.AddBroker(fmt.Sprintf("tcp://%s:%d", MQTTBroker, MQTTPort))
	client := mqtt.NewClient(opts)
	if token := client.Connect(); token.Wait() && token.Error() != nil {
		panic(token.Error())
	}
	t := client.Publish("functions/go", 0, false, input)
	<-t.Done() // Wait until message is published
	if t.Error() != nil {
		panic(t.Error())
	}

	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Message published!"))
}
Update a Thing's Property
The following example shows how to update a Thing's Property using the swx package to deal with token request and revocation.
package function 
 
import ( 
   "fmt" 
   "function/swx" 
   "function/swx/auth" 
   "io/ioutil" 
   "net/http" 
 
   "github.com/go-resty/resty/v2" 
) 
 
// We'll use a Thing credentials to request the access token 
var ( 
   ApiUrl       = "https://api.swx.altairone.com" 
   Space        = "examples" // Change it to your Space name 
   Category     = "<A-CATEGORY-NAME>" 
   ThingID      = "<A-THING-ID>" 
   ClientID     = "<A-CLIENT-ID>" 
   
) 
 


func Handle(w http.ResponseWriter, r *http.Request) {
  // Get the client secret from the Secrets Storage.
  // This avoids hardcoding the secret in the code.
  clientSecret := secrets.Get("my_secret_name")


  token, err := auth.GetToken(ClientID, clientSecret, []string{"thing.update"})
  if err != nil {
    w.WriteHeader(err.(*swx.OAuth2Error).Err.Status)
    return
  }
  defer token.Revoke() // This ensures the token is revoked before the function ends


  url := fmt.Sprintf("%s/spaces/%s/collections/%s/things/%s/properties",
    ApiUrl, Space, Collection, ThingID)


  client := resty.New()
  resp, err := client.R().
      SetAuthToken(token.AccessToken).
      SetBody(map[string]interface{}{"temperature": 27}).
      Put(url)
  if err != nil {
      w.WriteHeader(resp.StatusCode())
      w.Write(resp.Body())
      return
  }


  w.WriteHeader(http.StatusOK)
  w.Write(resp.Body())
}