DEV Community

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

Posted on • Originally published at couedeloalexandre.Medium

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

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

Top comments (0)