DEV Community

loading...
Cover image for Manage Static Assets with `embed` (Golang 1.16) - A SlackBot Use Case

Manage Static Assets with `embed` (Golang 1.16) - A SlackBot Use Case

Alexandre Couedelo
I embrace the DevOps culture since I started my career by contributing to the digital transformation of a leading financial institution in Canada.
Originally published at couedeloalexandre.Medium ・5 min read

Overview

Golang 1.16 new package embed helps you manage static assets, embedding them in the application binary and making them easy to use. Any files from a package or package subdirectory can be embedded and retrieved as a variable of type string or bytes[].

import _ "embed"

//go:embed hello.txt
var s strings

//go:embed hello.txt
var b []bytes
Enter fullscreen mode Exit fullscreen mode

Besides, you can also retrieve your embedded files with a variable of type FS. You can even define which file needs to be embedded in your application using a glob pathname.

import "embed"

//go:embed assets/*
var f embed.FS
Enter fullscreen mode Exit fullscreen mode

Official Documentation

Use case: Using embed in a SlackBot

Given how easy it is to create and edit your messages using Block-kit Builder, I believe that the most convenient method to design and maintain your SlackBot is to save the design created with Block-kit as a json payload. The new embed package is the perfect feature for This case.

In terms of design pattern, those json payloads represent the View in a classical MVC application. Besides, we can send those messages as is or use some templating to include any data.

In my tutorial series Slackbot in Golang with Socket Mode, I have used this method in all my Views in combination with go markup language. In this section, I will be demonstrating how to manage a greeting message designed with Block-kit. I will only focus on the View part of the application, ignoring the implementation of Model and Controller along. Nevertheless, feel free to peak at them in my git repository; Also, I am writing a set of articles covering those details.

Create a Message with Block-kit

In this step, no code required! Go to Block Kit Builder, customize the template, copy the json payload and, save it into a file. In my case: greeting.json.

Next, edit this template to make it customizable using go markup language. For instance, I want to add the name of the user that recieve the message, then the text for the message will likke like this:

Hi {{ .User }} :wave:
Enter fullscreen mode Exit fullscreen mode

After rendering the template, I would expect (if my user is called David)

Hi David :wave:
Enter fullscreen mode Exit fullscreen mode

Render the Message

First, let's use embed and declare a variable greetingAssets that refers to our asset folder.

import (
    "embed"
)

//go:embed greetingViewsAssets/*
var greetingAssets embed.FS
Enter fullscreen mode Exit fullscreen mode

Second, let's create a function that takes the user name as a string and returns a slice of slack.Block. Those slack.Block(s) represent the blocks we have created with Block-kit and saved into the file greetingViews/greeting.json. You can use them with the PostEphemeral function to send the greeting message to a user.

func GreetingMessage(user string) []slack.Block {

    // [TODO]: parse the template `greetingViews/greeting.json`

    view := slack.Msg{}

    // [TODO]: unmarshal the template into slack.Msg{}

    return view.Blocks.BlockSet
}
Enter fullscreen mode Exit fullscreen mode

Next, we want to render greetingViews/greeting.json using greetingAssets and the user name provided as input for our function. To do so, I created a small utility function because we might reuse it across our application. This function takes as arguments a variable of type fs.FS such as greetingAssets, the path of the file to use as a template and, a variable of type interface{} that represents any struct that contains data to interpolate in the template.

utils.go

func renderTemplate(fs fs.FS, file string, args interface{}) bytes.Buffer {

    var tpl bytes.Buffer

    // read the block-kit definition as a go template
    t, err := template.ParseFS(fs, file)
    if err != nil {
        panic(err)
    }

    // render the template using provided datas
    err = t.Execute(&tpl, args)
    if err != nil {
        panic(err)
    }

    return tpl
}

Enter fullscreen mode Exit fullscreen mode

Finally, we put all the pieces together:

  • read the block-kit definition as a go template and interpolate data
  • unmarshal the template into slack.Msg{}

greetingViews.go

func GreetingMessage(user string) []slack.Block {

    // we need a stuct to hold template arguments
    type args struct {
        User string
    }

    tpl := renderTemplate(greetingAssets, "greetingViews/greeting.json", args{User: user})

    // we convert the view into a message struct
    view := slack.Msg{}

    str, _ := ioutil.ReadAll(&tpl)
    json.Unmarshal(str, &view)

    // We only return the block because of the way the PostEphemeral function works
    // we are going to use slack.MsgOptionBlocks in the controller
    return view.Blocks.BlockSet
}

Enter fullscreen mode Exit fullscreen mode

Conclusion

The new Golang v1.16 embed directive lets us keep a single binary and bundle out static content. I like the convenience it offers when designing SlackBot using Block-kit.

The source code of the use case can be found here, as well as more use cases and work in progress ideas around creating SlackBots.

Interesting Articles tackling embed in a different context

Golang Slackbot Tutorial Series

Discussion (0)