Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion pkg/locale/localizedata/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,13 @@ ValueUpdated: "{{ .Field_1 }} value has been updated"
Cancelled: "{{ .Field_1 }} has been cancelled"
Failed: "{{ .Field_1 }} has failed"
Skipped: "{{ .Field_1 }} has been skipped"
AppliedNow: "{{ .Field_1 }} has been applied immediately"
AppliedNow: "{{ .Field_1 }} has been applied immediately"

#############################
# Subscription Notifications
#############################

NotificationFeatureStale: "There are feature flags that have not been used for more than {{ .Field_1 }} days."
NotificationExperimentRunning: "There are running experiments."
NotificationExperimentDaysLeft: "Days left: {{ .Field_1 }}"
NotificationMAUCount: "This is the MAU for month {{ .Field_1 }}."
11 changes: 10 additions & 1 deletion pkg/locale/localizedata/ja.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,13 @@ ValueUpdated: "{{ .Field_1 }}の値が更新されました"
Cancelled: "{{ .Field_1 }}がキャンセルされました"
Failed: "{{ .Field_1 }}が失敗しました"
Skipped: "{{ .Field_1 }}がスキップされました"
AppliedNow: "{{ .Field_1 }}が即時適用されました"
AppliedNow: "{{ .Field_1 }}が即時適用されました"

#############################
# Subscription Notifications
#############################

NotificationFeatureStale: "{{ .Field_1 }}日以上使用されていないフィーチャーフラグがあります。"
NotificationExperimentRunning: "実行中のエクスペリメントがあります。"
NotificationExperimentDaysLeft: "残り {{ .Field_1 }} 日"
NotificationMAUCount: "{{ .Field_1 }}月のMAUです。"
8 changes: 8 additions & 0 deletions pkg/locale/localizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,14 @@ const (
AppliedNowTemplate = "AppliedNow"
)

// subscription notifications
const (
NotificationFeatureStaleTemplate = "NotificationFeatureStale"
NotificationExperimentRunning = "NotificationExperimentRunning"
NotificationExperimentDaysLeftTemplate = "NotificationExperimentDaysLeft"
NotificationMAUCountTemplate = "NotificationMAUCount"
)

func init() {
bundle = i18n.NewBundle(language.English)
bundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal)
Expand Down
62 changes: 0 additions & 62 deletions pkg/subscription/sender/notifier/message.go

This file was deleted.

47 changes: 24 additions & 23 deletions pkg/subscription/sender/notifier/slack.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"encoding/json"
"errors"
"fmt"
"strconv"
"time"

"github.com/slack-go/slack"
Expand Down Expand Up @@ -174,11 +175,11 @@ func (n *slackNotifier) createAttachment(
case senderproto.Notification_DomainEvent:
return n.createDomainEventAttachment(notification.DomainEventNotification, localizer)
case senderproto.Notification_FeatureStale:
return n.createFeatureStaleAttachment(notification.FeatureStaleNotification)
return n.createFeatureStaleAttachment(notification.FeatureStaleNotification, localizer)
case senderproto.Notification_ExperimentRunning:
return n.createExperimentRunningAttachment(notification.ExperimentRunningNotification)
return n.createExperimentRunningAttachment(notification.ExperimentRunningNotification, localizer)
case senderproto.Notification_MauCount:
return n.createMAUCountAttachment(notification.MauCountNotification)
return n.createMAUCountAttachment(notification.MauCountNotification, localizer)
case senderproto.Notification_DemoOrganizationCreation:
return n.createDemoOrganizationCreationAttachment(notification.DemoOrganizationCreationNotification)
}
Expand Down Expand Up @@ -257,6 +258,7 @@ func (n *slackNotifier) createDomainEventAttachment(

func (n *slackNotifier) createFeatureStaleAttachment(
notification *senderproto.FeatureStaleNotification,
localizer locale.Localizer,
) (*slack.Attachment, error) {
featureListMsg := ""
for _, feature := range notification.Features {
Expand All @@ -272,16 +274,14 @@ func (n *slackNotifier) createFeatureStaleAttachment(
newLine := "- ID: `" + feature.Id + "`, Name: *" + fmt.Sprintf(linkTemplate, url, feature.Name) + "*\n"
featureListMsg = featureListMsg + newLine
}
// handle loc if multi-lang is necessary
msg, err := localizedMessage(msgTypeFeatureStale, locale.Ja)
if err != nil {
return nil, err
}
replacedMsg := fmt.Sprintf(msg.Message, featuredomain.SecondsToStale/24/60/60)
msg := localizer.MustLocalizeWithTemplate(
locale.NotificationFeatureStaleTemplate,
strconv.Itoa(featuredomain.SecondsToStale/24/60/60),
)
attachment := &slack.Attachment{
Color: "#F4D03F",
MarkdownIn: []string{"text"},
Text: replacedMsg + "\n\n" +
Text: msg + "\n\n" +
"Environment: " + notification.EnvironmentName + "\n\n" +
"Feature flags: \n\n" +
featureListMsg,
Expand All @@ -291,6 +291,7 @@ func (n *slackNotifier) createFeatureStaleAttachment(

func (n *slackNotifier) createExperimentRunningAttachment(
notification *senderproto.ExperimentRunningNotification,
localizer locale.Localizer,
) (*slack.Attachment, error) {
listMsg := ""
now := time.Now()
Expand All @@ -305,18 +306,18 @@ func (n *slackNotifier) createExperimentRunningAttachment(
return nil, err
}
nameLink := fmt.Sprintf(linkTemplate, url, e.Name)
newLine := fmt.Sprintf("- 残り `%d` 日, Name: *%s*\n", lastDays(now, time.Unix(e.StopAt, 0)), nameLink)
daysLeft := localizer.MustLocalizeWithTemplate(
locale.NotificationExperimentDaysLeftTemplate,
fmt.Sprintf("`%d`", lastDays(now, time.Unix(e.StopAt, 0))),
)
newLine := fmt.Sprintf("- %s, Name: *%s*\n", daysLeft, nameLink)
listMsg = listMsg + newLine
}
// handle loc if multi-lang is necessary
msg, err := localizedMessage(msgTypeExperimentResult, locale.Ja)
if err != nil {
return nil, err
}
msg := localizer.MustLocalize(locale.NotificationExperimentRunning)
attachment := &slack.Attachment{
Color: "#3498DB",
MarkdownIn: []string{"text"},
Text: msg.Message + "\n\n" +
Text: msg + "\n\n" +
"Environment: " + notification.EnvironmentName + "\n\n" +
"Experiments: \n\n" +
listMsg,
Expand All @@ -326,17 +327,17 @@ func (n *slackNotifier) createExperimentRunningAttachment(

func (n *slackNotifier) createMAUCountAttachment(
notification *senderproto.MauCountNotification,
localizer locale.Localizer,
) (*slack.Attachment, error) {
msg, err := localizedMessage(msgTypeMAUCount, locale.Ja)
if err != nil {
return nil, err
}
replacedMsg := fmt.Sprintf(msg.Message, notification.Month)
msg := localizer.MustLocalizeWithTemplate(
locale.NotificationMAUCountTemplate,
strconv.Itoa(int(notification.Month)),
)
p := message.NewPrinter(language.English)
attachment := &slack.Attachment{
Color: "#3498DB",
MarkdownIn: []string{"text"},
Text: replacedMsg + "\n\n" +
Text: msg + "\n\n" +
"Environment: " + notification.EnvironmentName + "\n" +
p.Sprintf("Event count: %d", notification.EventCount) + "\n" +
p.Sprintf("User count: %d", notification.UserCount),
Expand Down
141 changes: 141 additions & 0 deletions pkg/subscription/sender/notifier/slack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,19 @@ package notifier

import (
"context"
"strconv"
"testing"
"time"

"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"google.golang.org/grpc/metadata"

featuredomain "github.com/bucketeer-io/bucketeer/v2/pkg/feature/domain"
"github.com/bucketeer-io/bucketeer/v2/pkg/locale"
domainproto "github.com/bucketeer-io/bucketeer/v2/proto/event/domain"
experimentproto "github.com/bucketeer-io/bucketeer/v2/proto/experiment"
featureproto "github.com/bucketeer-io/bucketeer/v2/proto/feature"
senderproto "github.com/bucketeer-io/bucketeer/v2/proto/subscription/sender"
)

Expand Down Expand Up @@ -136,3 +140,140 @@ func TestCreateDomainEventAttachment(t *testing.T) {
})
}
}

func newTestLocalizer(lang string) locale.Localizer {
md := metadata.New(map[string]string{"accept-language": lang})
return locale.NewLocalizer(metadata.NewIncomingContext(context.Background(), md))
}

func TestCreateFeatureStaleAttachment(t *testing.T) {
t.Parallel()

staleDays := strconv.Itoa(featuredomain.SecondsToStale / 24 / 60 / 60)

patterns := []struct {
desc string
lang string
expectedText string
}{
{
desc: "english",
lang: locale.En,
expectedText: "There are feature flags that have not been used for more than " + staleDays + " days.",
},
{
desc: "japanese",
lang: locale.Ja,
expectedText: staleDays + "日以上使用されていないフィーチャーフラグがあります。",
},
}

for _, p := range patterns {
t.Run(p.desc, func(t *testing.T) {
t.Parallel()
notifier := &slackNotifier{webURL: "https://example.com", logger: zap.NewNop()}
attachment, err := notifier.createFeatureStaleAttachment(
&senderproto.FeatureStaleNotification{
EnvironmentName: "test-env",
EnvironmentUrlCode: "test",
Features: []*featureproto.Feature{
{Id: "feature-id-1", Name: "feature-name-1"},
},
},
newTestLocalizer(p.lang),
)
assert.NoError(t, err)
assert.Contains(t, attachment.Text, p.expectedText)
assert.Contains(t, attachment.Text, "https://example.com/test/features/feature-id-1")
})
}
}

func TestCreateExperimentRunningAttachment(t *testing.T) {
t.Parallel()

patterns := []struct {
desc string
lang string
expectedText string
expectedDaysMsg string
}{
{
desc: "english",
lang: locale.En,
expectedText: "There are running experiments.",
expectedDaysMsg: "- Days left: `1`, Name: *",
},
{
desc: "japanese",
lang: locale.Ja,
expectedText: "実行中のエクスペリメントがあります。",
expectedDaysMsg: "- 残り `1` 日, Name: *",
},
}

for _, p := range patterns {
t.Run(p.desc, func(t *testing.T) {
t.Parallel()
notifier := &slackNotifier{webURL: "https://example.com", logger: zap.NewNop()}
attachment, err := notifier.createExperimentRunningAttachment(
&senderproto.ExperimentRunningNotification{
EnvironmentName: "test-env",
EnvironmentUrlCode: "test",
Experiments: []*experimentproto.Experiment{
{
Id: "experiment-id-1",
Name: "experiment-name-1",
StopAt: time.Now().Add(25 * time.Hour).Unix(),
},
},
},
newTestLocalizer(p.lang),
)
assert.NoError(t, err)
assert.Contains(t, attachment.Text, p.expectedText)
assert.Contains(t, attachment.Text, p.expectedDaysMsg)
})
}
}

func TestCreateMAUCountAttachment(t *testing.T) {
t.Parallel()

patterns := []struct {
desc string
lang string
expectedText string
}{
{
desc: "english",
lang: locale.En,
expectedText: "This is the MAU for month 4.",
},
{
desc: "japanese",
lang: locale.Ja,
expectedText: "4月のMAUです。",
},
}

for _, p := range patterns {
t.Run(p.desc, func(t *testing.T) {
t.Parallel()
notifier := &slackNotifier{webURL: "https://example.com", logger: zap.NewNop()}
attachment, err := notifier.createMAUCountAttachment(
&senderproto.MauCountNotification{
EnvironmentName: "test-env",
EventCount: 1234,
UserCount: 567,
Month: 4,
},
newTestLocalizer(p.lang),
)
assert.NoError(t, err)
assert.Contains(t, attachment.Text, p.expectedText)
assert.Contains(t, attachment.Text, "Event count: 1,234")
assert.Contains(t, attachment.Text, "User count: 567")
})
}
}
Loading