Skip to content

Commit

Permalink
Merge pull request #27 from is2ei/support-typetalk
Browse files Browse the repository at this point in the history
Support Typetalk
  • Loading branch information
b4b4r07 authored Jan 16, 2019
2 parents 1ed5ff1 + 7a1347c commit 12d2630
Show file tree
Hide file tree
Showing 13 changed files with 421 additions and 3 deletions.
1 change: 1 addition & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ ignore:
- "main.go"
- "notifier/github/github.go"
- "notifier/slack/slack.go"
- "notifier/typetalk/typetalk.go"
14 changes: 14 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ You can do this by using this command.

<img src="./misc/images/2.png" width="500">

<img src="./misc/images/3.png" width="600">

## Installation

Grab the binary from GitHub Releases (Recommended)
Expand Down Expand Up @@ -60,7 +62,7 @@ For `plan` command, you also need to specify `plan` as the argument of tfnotify.

When running tfnotify, you can specify the configuration path via `--config` option (if it's omitted, it defaults to `{.,}tfnotify.y{,a}ml`).

The example settings of GitHub, GitHub Enterprise and Slack are as follows. Incidentally, there is no need to replace TOKEN string such as `$GITHUB_TOKEN` with the actual token. Instead, it must be defined as environment variables in CI settings.
The example settings of GitHub and GitHub Enterprise, Slack, [Typetalk](https://www.typetalk.com/) are as follows. Incidentally, there is no need to replace TOKEN string such as `$GITHUB_TOKEN` with the actual token. Instead, it must be defined as environment variables in CI settings.

[template](https://golang.org/pkg/text/template/) of Go can be used for `template`. The templates can be used in `tfnotify.yaml` are as follows:

Expand Down Expand Up @@ -198,6 +200,32 @@ terraform:
</details>
<details>
<summary>For Typetalk</summary>
```yaml
---
ci: circleci
notifier:
typetalk:
token: $TYPETALK_TOKEN
topic_id: $TYPETALK_TOPIC_ID
terraform:
plan:
template: |
{{ .Message }}
{{if .Result}}
```
{{ .Result }}
```
{{end}}
```
{{ .Body }}
```
```
</details>
### Supported CI
Currently, supported CI are here:
Expand Down
24 changes: 22 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ type Config struct {

// Notifier is a notification notifier
type Notifier struct {
Github GithubNotifier `yaml:"github"`
Slack SlackNotifier `yaml:"slack"`
Github GithubNotifier `yaml:"github"`
Slack SlackNotifier `yaml:"slack"`
Typetalk TypetalkNotifier `yaml:"typetalk"`
}

// GithubNotifier is a notifier for GitHub
Expand All @@ -45,6 +46,12 @@ type SlackNotifier struct {
Bot string `yaml:"bot"`
}

// TypetalkNotifier is a notifier for Typetalk
type TypetalkNotifier struct {
Token string `yaml:"token"`
TopicID string `yaml:"topic_id"`
}

// Terraform represents terraform configurations
type Terraform struct {
Default Default `yaml:"default"`
Expand Down Expand Up @@ -111,6 +118,11 @@ func (cfg *Config) Validation() error {
return fmt.Errorf("slack channel id is missing")
}
}
if cfg.isDefinedTypetalk() {
if cfg.Notifier.Typetalk.TopicID == "" {
return fmt.Errorf("Typetalk topic id is missing")
}
}
notifier := cfg.GetNotifierType()
if notifier == "" {
return fmt.Errorf("notifier is missing")
Expand All @@ -128,6 +140,11 @@ func (cfg *Config) isDefinedSlack() bool {
return cfg.Notifier.Slack != (SlackNotifier{})
}

func (cfg *Config) isDefinedTypetalk() bool {
// not empty
return cfg.Notifier.Typetalk != (TypetalkNotifier{})
}

// GetNotifierType return notifier type described in Config
func (cfg *Config) GetNotifierType() string {
if cfg.isDefinedGithub() {
Expand All @@ -136,6 +153,9 @@ func (cfg *Config) GetNotifierType() string {
if cfg.isDefinedSlack() {
return "slack"
}
if cfg.isDefinedTypetalk() {
return "typetalk"
}
return ""
}

Expand Down
39 changes: 39 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ func TestLoadFile(t *testing.T) {
Channel: "",
Bot: "",
},
Typetalk: TypetalkNotifier{
Token: "",
TopicID: "",
},
},
Terraform: Terraform{
Default: Default{
Expand Down Expand Up @@ -73,6 +77,10 @@ func TestLoadFile(t *testing.T) {
Channel: "",
Bot: "",
},
Typetalk: TypetalkNotifier{
Token: "",
TopicID: "",
},
},
Terraform: Terraform{
Default: Default{
Expand Down Expand Up @@ -186,6 +194,33 @@ notifier:
slack:
token: token
channel: channel
`),
expected: "",
},
{
contents: []byte(`
ci: circleci
notifier:
typetalk:
`),
expected: "notifier is missing",
},
{
contents: []byte(`
ci: circleci
notifier:
typetalk:
token: token
`),
expected: "Typetalk topic id is missing",
},
{
contents: []byte(`
ci: circleci
notifier:
typetalk:
token: token
topic_id: 12345
`),
expected: "",
},
Expand Down Expand Up @@ -221,6 +256,10 @@ func TestGetNotifierType(t *testing.T) {
contents: []byte("repository:\n owner: a\n name: b\nci: circleci\nnotifier:\n slack:\n token: token\n"),
expected: "slack",
},
{
contents: []byte("repository:\n owner: a\n name: b\nci: circleci\nnotifier:\n typetalk:\n token: token\n"),
expected: "typetalk",
},
}
for _, testCase := range testCases {
cfg, err := helperLoadConfig(testCase.contents)
Expand Down
15 changes: 15 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/mercari/tfnotify/notifier"
"github.com/mercari/tfnotify/notifier/github"
"github.com/mercari/tfnotify/notifier/slack"
"github.com/mercari/tfnotify/notifier/typetalk"
"github.com/mercari/tfnotify/terraform"

"github.com/urfave/cli"
Expand Down Expand Up @@ -100,6 +101,20 @@ func (t *tfnotify) Run() error {
return err
}
notifier = client.Notify
case "typetalk":
client, err := typetalk.NewClient(typetalk.Config{
Token: t.config.Notifier.Typetalk.Token,
TopicID: t.config.Notifier.Typetalk.TopicID,
Title: t.context.String("title"),
Message: t.context.String("message"),
CI: ci.URL,
Parser: t.parser,
Template: t.template,
})
if err != nil {
return err
}
notifier = client.Notify
case "":
return fmt.Errorf("notifier is missing")
default:
Expand Down
Binary file added misc/images/3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 changes: 79 additions & 0 deletions notifier/typetalk/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package typetalk

import (
"errors"
"os"
"strconv"

"github.com/mercari/tfnotify/terraform"
typetalk "github.com/nulab/go-typetalk/typetalk/v1"
)

// EnvToken is Typetalk API Token
const EnvToken = "TYPETALK_TOKEN"

// EnvTopicID is Typetalk topic ID
const EnvTopicID = "TYPETALK_TOPIC_ID"

// Client represents Typetalk API client.
type Client struct {
*typetalk.Client
Config Config
common service
Notify *NotifyService
API API
}

// Config is a configuration for Typetalk Client
type Config struct {
Token string
Title string
TopicID string
Message string
CI string
Parser terraform.Parser
Template terraform.Template
}

type service struct {
client *Client
}

// NewClient returns Client initialized with Config
func NewClient(cfg Config) (*Client, error) {
token := os.ExpandEnv(cfg.Token)
if token == EnvToken {
token = os.Getenv(EnvToken)
}
if token == "" {
return &Client{}, errors.New("Typetalk token is missing")
}

topicIDString := os.ExpandEnv(cfg.TopicID)
if topicIDString == EnvTopicID {
topicIDString = os.Getenv(EnvTopicID)
}
if topicIDString == "" {
return &Client{}, errors.New("Typetalk topic ID is missing")
}

topicID, err := strconv.Atoi(topicIDString)
if err != nil {
return &Client{}, errors.New("Typetalk topic ID is not numeric value")
}

client := typetalk.NewClient(nil)
client.SetTypetalkToken(token)
c := &Client{
Config: cfg,
Client: client,
}
c.common.client = c
c.Notify = (*NotifyService)(&c.common)
c.API = &Typetalk{
Client: client,
TopicID: topicID,
}

return c, nil
}
73 changes: 73 additions & 0 deletions notifier/typetalk/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package typetalk

import (
"os"
"testing"
)

func TestNewClient(t *testing.T) {
typetalkToken := os.Getenv(EnvToken)
defer func() {
os.Setenv(EnvToken, typetalkToken)
}()
os.Setenv(EnvToken, "")

testCases := []struct {
config Config
envToken string
expect string
}{
{
// specify directly
config: Config{Token: "abcdefg", TopicID: "12345"},
envToken: "",
expect: "",
},
{
// specify via env but not to be set env (part 1)
config: Config{Token: "TYPETALK_TOKEN", TopicID: "12345"},
envToken: "",
expect: "Typetalk token is missing",
},
{
// specify via env (part 1)
config: Config{Token: "TYPETALK_TOKEN", TopicID: "12345"},
envToken: "abcdefg",
expect: "",
},
{
// specify via env but not to be set env (part 2)
config: Config{Token: "$TYPETALK_TOKEN", TopicID: "12345"},
envToken: "",
expect: "typetalk token is missing",
},
{
// specify via env (part 2)
config: Config{Token: "$TYPETALK_TOKEN", TopicID: "12345"},
envToken: "abcdefg",
expect: "",
},
{
// no specification (part 1)
config: Config{TopicID: "12345"},
envToken: "",
expect: "Typetalk token is missing",
},
{
// no specification (part 2)
config: Config{TopicID: "12345"},
envToken: "abcdefg",
expect: "Typetalk token is missing",
},
}
for _, testCase := range testCases {
os.Setenv(EnvToken, testCase.envToken)
_, err := NewClient(testCase.config)
if err == nil {
continue
}
if err.Error() != testCase.expect {
t.Errorf("got %q but want %q", err.Error(), testCase.expect)
}
}
}
Loading

0 comments on commit 12d2630

Please sign in to comment.