From 7930979adb4f6c468753213c5ce003d53a8edba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20D=C3=B6tsch?= Date: Thu, 9 Jan 2020 20:20:42 +0100 Subject: [PATCH] custom variables which can be used in macros etc --- command/commands.go | 2 + command/macro.go | 2 +- command/variables/add.go | 22 +++++ command/variables/command.go | 34 ++++++++ command/variables/delete.go | 17 ++++ command/variables/integration_test.go | 112 ++++++++++++++++++++++++++ command/variables/list.go | 23 ++++++ command/variables/storage.go | 22 +++++ command/variables/template.go | 20 +++++ readme.md | 19 +++++ 10 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 command/variables/add.go create mode 100644 command/variables/command.go create mode 100644 command/variables/delete.go create mode 100644 command/variables/integration_test.go create mode 100644 command/variables/list.go create mode 100644 command/variables/storage.go create mode 100644 command/variables/template.go diff --git a/command/commands.go b/command/commands.go index 14121e7b..366bd4b3 100644 --- a/command/commands.go +++ b/command/commands.go @@ -13,6 +13,7 @@ import ( "github.com/innogames/slack-bot/command/mqtt" "github.com/innogames/slack-bot/command/pullrequest" "github.com/innogames/slack-bot/command/queue" + "github.com/innogames/slack-bot/command/variables" "github.com/innogames/slack-bot/command/weather" "github.com/sirupsen/logrus" ) @@ -49,6 +50,7 @@ func GetCommands(slackClient client.SlackClient, cfg config.Config, logger *logr queue.NewListCommand(slackClient), custom.GetCommand(slackClient), + variables.GetCommand(slackClient), ) // jenkins diff --git a/command/macro.go b/command/macro.go index e6f7bdc5..7cbbfb4d 100644 --- a/command/macro.go +++ b/command/macro.go @@ -56,7 +56,7 @@ func (c *macroCommand) Execute(event slack.MessageEvent) bool { // extract the parameters from regexp params := util.RegexpResultToParams(macro.re, match) - params["sender"] = event.User + params["userId"] = event.User for _, commandText := range macro.config.Commands { command, err := util.CompileTemplate(commandText) diff --git a/command/variables/add.go b/command/variables/add.go new file mode 100644 index 00000000..97a3871e --- /dev/null +++ b/command/variables/add.go @@ -0,0 +1,22 @@ +package variables + +import ( + "fmt" + + "github.com/innogames/slack-bot/bot/matcher" + "github.com/nlopes/slack" +) + +func (c *command) Add(match matcher.Result, event slack.MessageEvent) { + name := match.GetString("name") + value := match.GetString("value") + + list := loadList(event.User) + list[name] = value + storeList(event, list) + + c.slackClient.Reply( + event, + fmt.Sprintf("Added variable: `%s` = `%s`.", name, value), + ) +} diff --git a/command/variables/command.go b/command/variables/command.go new file mode 100644 index 00000000..eaf2ad30 --- /dev/null +++ b/command/variables/command.go @@ -0,0 +1,34 @@ +package variables + +import ( + "github.com/innogames/slack-bot/bot" + "github.com/innogames/slack-bot/bot/matcher" + "github.com/innogames/slack-bot/client" +) + +// GetCommand returns a set of all commands to manage user specific variables +func GetCommand(slackClient client.SlackClient) bot.Command { + return &command{slackClient} +} + +type command struct { + slackClient client.SlackClient +} + +func (c *command) GetMatcher() matcher.Matcher { + return matcher.NewGroupMatcher( + matcher.NewRegexpMatcher("(add|set) variable '?(?P.*?)'? '?(?P.*?)'?", c.Add), + matcher.NewRegexpMatcher("(delete|remove) variable '?(?P.*?)'?", c.Delete), + matcher.NewTextMatcher("list variables", c.List), + ) +} + +func (c *command) GetHelp() []bot.Help { + return []bot.Help{ + { + "custom variables", + "", + []string{}, + }, + } +} diff --git a/command/variables/delete.go b/command/variables/delete.go new file mode 100644 index 00000000..681f4b6b --- /dev/null +++ b/command/variables/delete.go @@ -0,0 +1,17 @@ +package variables + +import ( + "fmt" + "github.com/innogames/slack-bot/bot/matcher" + "github.com/nlopes/slack" +) + +func (c *command) Delete(match matcher.Result, event slack.MessageEvent) { + name := match.GetString("name") + + list := loadList(event.User) + delete(list, name) + storeList(event, list) + + c.slackClient.Reply(event, fmt.Sprintf("Okay, I deleted variable: `%s`", name)) +} diff --git a/command/variables/integration_test.go b/command/variables/integration_test.go new file mode 100644 index 00000000..188ed1fd --- /dev/null +++ b/command/variables/integration_test.go @@ -0,0 +1,112 @@ +package variables + +import ( + "testing" + + "github.com/innogames/slack-bot/bot" + "github.com/innogames/slack-bot/mocks" + "github.com/nlopes/slack" + "github.com/stretchr/testify/assert" +) + +func TestCustomCommands(t *testing.T) { + slackClient := &mocks.SlackClient{} + commands := bot.Commands{} + variablesCommand := GetCommand(slackClient).(*command) + commands.AddCommand(variablesCommand) + + t.Run("Invalid command", func(t *testing.T) { + event := slack.MessageEvent{} + event.User = "user1" + event.Text = "notify me not" + + actual := commands.Run(event) + assert.Equal(t, false, actual) + }) + + t.Run("List empty variables", func(t *testing.T) { + event := slack.MessageEvent{} + event.Text = "list variables" + event.User = "user1" + slackClient.On("Reply", event, "No variables define yet. Use `add variable 'defaultServer' 'beta'`").Return("") + actual := commands.Run(event) + assert.Equal(t, true, actual) + }) + + t.Run("Add a variable with invalid syntax", func(t *testing.T) { + event := slack.MessageEvent{} + event.User = "user1" + event.Text = "add variable name" + actual := commands.Run(event) + assert.Equal(t, false, actual) + }) + + t.Run("Add valid variable", func(t *testing.T) { + event := slack.MessageEvent{} + event.User = "user1" + event.Text = "add variable 'myKey' 'myValue'" + + slackClient.On("Reply", event, "Added variable: `myKey` = `myValue`.").Return("") + actual := commands.Run(event) + + assert.Equal(t, true, actual) + }) + + t.Run("List commands should list new variable", func(t *testing.T) { + event := slack.MessageEvent{} + event.User = "user1" + event.Text = "list variables" + + slackClient.On("Reply", event, "You defined 1 variables:\n - myKey: `myValue`") + actual := commands.Run(event) + + assert.Equal(t, true, actual) + }) + + t.Run("Template with unknown user", func(t *testing.T) { + + function := variablesCommand.GetTemplateFunction()["customVariable"] + + actual := function.(func(string, string) string)("U123", "myKey") + assert.Equal(t, "_unknown variable: myKey_", actual) + }) + + t.Run("Template with unknown user", func(t *testing.T) { + + function := variablesCommand.GetTemplateFunction()["customVariable"] + + actual := function.(func(string, string) string)("user1", "myKey2") + assert.Equal(t, "_unknown variable: myKey2_", actual) + }) + + t.Run("Template with known variable", func(t *testing.T) { + + function := variablesCommand.GetTemplateFunction()["customVariable"] + + actual := function.(func(string, string) string)("user1", "myKey") + assert.Equal(t, "myValue", actual) + }) + + t.Run("Delete variable with invalid syntax", func(t *testing.T) { + event := slack.MessageEvent{} + event.Text = "delete variable" + event.User = "user1" + + actual := commands.Run(event) + + assert.Equal(t, false, actual) + }) + + t.Run("Delete variable", func(t *testing.T) { + event := slack.MessageEvent{} + event.Text = "delete variable myKey" + event.User = "user1" + + slackClient.On("Reply", event, "Okay, I deleted variable: `myKey`") + + actual := commands.Run(event) + + assert.Equal(t, true, actual) + }) + +} diff --git a/command/variables/list.go b/command/variables/list.go new file mode 100644 index 00000000..337b8ca6 --- /dev/null +++ b/command/variables/list.go @@ -0,0 +1,23 @@ +package variables + +import ( + "fmt" + + "github.com/innogames/slack-bot/bot/matcher" + "github.com/nlopes/slack" +) + +func (c *command) List(match matcher.Result, event slack.MessageEvent) { + list := loadList(event.User) + if len(list) == 0 { + c.slackClient.Reply(event, "No variables define yet. Use `add variable 'defaultServer' 'beta'`") + return + } + + responseText := fmt.Sprintf("You defined %d variables:", len(list)) + for name, value := range list { + responseText += fmt.Sprintf("\n - %s: `%s`", name, value) + } + + c.slackClient.Reply(event, responseText) +} diff --git a/command/variables/storage.go b/command/variables/storage.go new file mode 100644 index 00000000..f54119f1 --- /dev/null +++ b/command/variables/storage.go @@ -0,0 +1,22 @@ +package variables + +import ( + "github.com/innogames/slack-bot/bot/storage" + "github.com/nlopes/slack" +) + +const storeKey = "user_variables" + +type list map[string]string + +func loadList(userId string) list { + list := make(list, 0) + + storage.Read(storeKey, userId, &list) + + return list +} + +func storeList(event slack.MessageEvent, list list) { + storage.Write(storeKey, event.User, list) +} diff --git a/command/variables/template.go b/command/variables/template.go new file mode 100644 index 00000000..65997c0c --- /dev/null +++ b/command/variables/template.go @@ -0,0 +1,20 @@ +package variables + +import ( + "fmt" + "text/template" +) + +func (c *command) GetTemplateFunction() template.FuncMap { + return template.FuncMap{ + "customVariable": func(userId string, name string) string { + list := loadList(userId) + + if value, ok := list[name]; ok { + return value + } + + return fmt.Sprintf("_unknown variable: %s_", name) + }, + } +} diff --git a/readme.md b/readme.md index 55a28f36..fa0abe59 100644 --- a/readme.md +++ b/readme.md @@ -125,6 +125,25 @@ It's also possible to get a notification when there is a state change in a certa **Example** - `watch ticket PROJ-12234` + +## Custom variables +Configure user specific variables to customize bot behaviour. E.g. each developer has his own server environment. + +**Example:** Having this global config: +``` +macros: + - name: deploy + trigger: "deploy (?P.*)" + commands: + - deploy {{.branch}} to {{ customVariable "defaultServer" }} +``` + +User can define his default environment once by using `set variable serverEnvironment aws-02`. + +Then the `deploy feature-123` will deploy the branch to the defined `aws-02` environment. +Each user can define his own variables. + + ## Quiz command If you need a small break and want to play a little quiz game you can do so by calling this command. No more than 50 questions are allowed.