From 3349149ec930f21ea606bd036289361204c3efe0 Mon Sep 17 00:00:00 2001 From: Zachary Tsang Date: Thu, 15 Aug 2024 10:48:18 +0800 Subject: [PATCH] Switch channelInfo encoding from string to JSON --- pkg/slack/app.go | 93 ++++++++++++++++++++++++--------------------- pkg/slack/filter.go | 31 ++++++++++++--- 2 files changed, 75 insertions(+), 49 deletions(-) diff --git a/pkg/slack/app.go b/pkg/slack/app.go index 1308c25..10bdd3a 100644 --- a/pkg/slack/app.go +++ b/pkg/slack/app.go @@ -2,6 +2,7 @@ package slack import ( "context" + "encoding/json" "errors" "fmt" "regexp" @@ -13,7 +14,6 @@ import ( "github.com/slack-go/slack/socketmode" "go.uber.org/zap" "golang.org/x/sync/errgroup" - "k8s.io/utils/strings/slices" ) var repoRegex = regexp.MustCompile("[a-zA-Z0-9-]+(/[a-zA-Z0-9-]+)?") @@ -31,6 +31,28 @@ type ChannelInfo struct { conclusions []string } +type exposedChannelInfo struct { + ChannelID string `json:"channelID"` + Conclusions []string `json:"messageFilters"` +} + +func (f ChannelInfo) MarshalJSON() ([]byte, error) { + return json.Marshal(exposedChannelInfo{ + ChannelID: f.channelID, + Conclusions: f.conclusions, + }) +} + +func (f *ChannelInfo) UnmarshalJSON(data []byte) error { + aux := &exposedChannelInfo{} + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + f.channelID = aux.ChannelID + f.conclusions = aux.Conclusions + return nil +} + func NewApp(logger *zap.Logger, config *Config, store kv.Store) *App { logger = logger.Named("slack-app") return &App{ @@ -50,13 +72,12 @@ func (a *App) Disabled() bool { return a.disabled } -// Format of channel info string: ":," -func toChannelInfoString(channelInfo ChannelInfo) string { - if len(channelInfo.conclusions) == 0 { - return channelInfo.channelID +func toChannelInfoString(channelInfo ChannelInfo) (string, error) { + json, err := json.Marshal(channelInfo) + if err != nil { + return "", err } - conclusionsString := strings.Join(channelInfo.conclusions, ",") - return channelInfo.channelID + ":" + conclusionsString + return string(json), nil } func (a *App) GetChannels(ctx context.Context, repo string) ([]ChannelInfo, error) { @@ -66,23 +87,12 @@ func (a *App) GetChannels(ctx context.Context, repo string) ([]ChannelInfo, erro } else if data == "" { return nil, nil } - channelInfoStrings := strings.Split(data, ";") - var channelInfos []ChannelInfo - for _, channelString := range channelInfoStrings { - channelID, conclusionsString, _ := strings.Cut(channelString, ":") - var conclusions []string - for _, conclusion := range strings.Split(conclusionsString, ",") { - if len(conclusion) > 0 { - conclusions = append(conclusions, conclusion) - } - } - channelInfos = append(channelInfos, ChannelInfo{ - channelID: channelID, - conclusions: conclusions, - }) + var channelInfos []ChannelInfo + err = json.Unmarshal([]byte(data), &channelInfos) + if err != nil { + return nil, err } - return channelInfos, nil } @@ -92,31 +102,22 @@ func (a *App) AddChannel(ctx context.Context, repo string, channelInfo ChannelIn return err } - // Ref: https://docs.github.com/en/rest/checks/runs?apiVersion=2022-11-28#create-a-check-run--parameters - supportedConclusions := []string{"action_required", "cancelled", "failure", "neutral", "success", "skipped", "stale", "timed_out"} - var unsupportedConclusions []string - for _, c := range channelInfo.conclusions { - if !slices.Contains(supportedConclusions, c) { - unsupportedConclusions = append(unsupportedConclusions, c) - } - } - - if len(unsupportedConclusions) > 0 { - return fmt.Errorf("unsupported conclusions: %s", strings.Join(unsupportedConclusions, ", ")) - } - - var newChannelInfoStrings []string + var newChannelInfos []ChannelInfo for _, c := range channelInfos { if c.channelID == channelInfo.channelID { // Skip the old subscription and will replace with the new conclusion filter options continue } - newChannelInfoStrings = append(newChannelInfoStrings, toChannelInfoString(c)) + newChannelInfos = append(newChannelInfos, c) } - newChannelInfoStrings = append(newChannelInfoStrings, toChannelInfoString(channelInfo)) - data := strings.Join(newChannelInfoStrings, ";") + newChannelInfos = append(newChannelInfos, channelInfo) - return a.store.Set(ctx, kvNamespace, repo, data) + data, err := json.Marshal(newChannelInfos) + if err != nil { + return err + } + + return a.store.Set(ctx, kvNamespace, repo, string(data)) } func (a *App) DelChannel(ctx context.Context, repo string, channelID string) error { @@ -125,21 +126,25 @@ func (a *App) DelChannel(ctx context.Context, repo string, channelID string) err return err } - var newChannelInfoStrings []string + var newChannelInfos []ChannelInfo found := false for _, c := range channelInfos { if c.channelID == channelID { found = true continue } - newChannelInfoStrings = append(newChannelInfoStrings, toChannelInfoString(c)) + newChannelInfos = append(newChannelInfos, c) } if !found { return fmt.Errorf("not subscribed to repo") } - data := strings.Join(newChannelInfoStrings, ";") - return a.store.Set(ctx, kvNamespace, repo, data) + data, err := json.Marshal(newChannelInfos) + if err != nil { + return err + } + + return a.store.Set(ctx, kvNamespace, repo, string(data)) } func (a *App) SendMessage(ctx context.Context, channel string, options ...slack.MsgOption) error { diff --git a/pkg/slack/filter.go b/pkg/slack/filter.go index f2af01f..09ee69c 100644 --- a/pkg/slack/filter.go +++ b/pkg/slack/filter.go @@ -1,6 +1,7 @@ package slack import ( + "encoding/json" "fmt" "strings" @@ -9,19 +10,39 @@ import ( ) type messageFilterRule struct { - conclusions []string + Conclusions []string `json:"conclusions"` // branches []string - workflows []string + Workflows []string `json:"workflows"` } + type MessageFilter struct { whitelists []messageFilterRule } +type exposedMessageFilter struct { + Whitelists []messageFilterRule `json:"filters"` +} + +func (mf MessageFilter) MarshalJSON() ([]byte, error) { + return json.Marshal(exposedMessageFilter{ + Whitelists: mf.whitelists, + }) +} + +func (mf *MessageFilter) UnmarshalJSON(data []byte) error { + aux := &exposedMessageFilter{} + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + mf.whitelists = aux.Whitelists + return nil +} + func (rule messageFilterRule) Pass(run *jobs.WorkflowRun) bool { - if len(rule.conclusions) > 0 && !slices.Contains(rule.conclusions, run.Conclusion) { + if len(rule.Conclusions) > 0 && !slices.Contains(rule.Conclusions, run.Conclusion) { return false } - if len(rule.workflows) > 0 && !slices.Contains(rule.workflows, run.Name) { + if len(rule.Workflows) > 0 && !slices.Contains(rule.Workflows, run.Name) { return false } return true @@ -59,7 +80,7 @@ func (rule *messageFilterRule) setConclusions(conclusions []string) error { return fmt.Errorf("unsupported conclusions: %s", strings.Join(unsupportedConclusions, ", ")) } - rule.conclusions = conclusions + rule.Conclusions = conclusions return nil }