Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add nil check #70

Merged
merged 5 commits into from
Sep 14, 2023
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
2 changes: 1 addition & 1 deletion cli/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func runJobCmd() *cobra.Command {
logger := log.NewLogrus(log.LogrusWithLevel(config.LogLevel))
crypto := crypto.NewAES(config.EncryptionSecretKeyKey)
validator := validator.New()
notifier, err := notifiers.NewClient(&config.Notifier)
notifier, err := notifiers.NewClient(&config.Notifier, logger)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func RunServer(config *Config) error {
logger := log.NewLogrus(log.LogrusWithLevel(config.LogLevel))
crypto := crypto.NewAES(config.EncryptionSecretKeyKey)
validator := validator.New()
notifier, err := notifiers.NewClient(&config.Notifier)
notifier, err := notifiers.NewClient(&config.Notifier, logger)
if err != nil {
return err
}
Expand Down
33 changes: 24 additions & 9 deletions plugins/notifiers/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package notifiers

import (
"errors"
"fmt"
"github.com/goto/salt/log"
"github.com/mitchellh/mapstructure"
"net/http"
"time"

Expand All @@ -17,18 +20,25 @@ const (
ProviderTypeSlack = "slack"
)

// SlackConfig is a map of workspace name to config
type SlackConfig map[string]interface{}

func (c SlackConfig) Decode(v interface{}) error {
return mapstructure.Decode(c, v)
}

type Config struct {
Provider string `mapstructure:"provider" validate:"omitempty,oneof=slack"`

// slack
AccessToken string `mapstructure:"access_token" validate:"required_without=Workspaces"`
Workspaces []slack.SlackWorkspace `mapstructure:"workspaces" validate:"required_without=AccessToken,dive"`
AccessToken string `mapstructure:"access_token" validate:"required_without=SlackConfig"`
SlackConfig SlackConfig `mapstructure:"slack_config" validate:"required_without=AccessToken,dive"`

// custom messages
Messages domain.NotificationMessages
}

func NewClient(config *Config) (Client, error) {
func NewClient(config *Config, logger *log.Logrus) (Client, error) {
if config.Provider == ProviderTypeSlack {

slackConfig, err := NewSlackConfig(config)
Expand All @@ -37,7 +47,7 @@ func NewClient(config *Config) (Client, error) {
}

httpClient := &http.Client{Timeout: 10 * time.Second}
return slack.NewNotifier(slackConfig, httpClient), nil
return slack.NewNotifier(slackConfig, httpClient, logger), nil
}

return nil, errors.New("invalid notifier provider type")
Expand All @@ -46,11 +56,11 @@ func NewClient(config *Config) (Client, error) {
func NewSlackConfig(config *Config) (*slack.Config, error) {

// validation
if config.AccessToken == "" && len(config.Workspaces) == 0 {
return nil, errors.New("slack access token or workspaces must be provided")
if config.AccessToken == "" && config.SlackConfig == nil {
return nil, errors.New("slack access token or workSpaceConfig must be provided")
}
if config.AccessToken != "" && len(config.Workspaces) != 0 {
return nil, errors.New("slack access token and workspaces cannot be provided at the same time")
if config.AccessToken != "" && config.SlackConfig != nil {
return nil, errors.New("slack access token and workSpaceConfig cannot be provided at the same time")
}

var slackConfig *slack.Config
Expand All @@ -69,8 +79,13 @@ func NewSlackConfig(config *Config) (*slack.Config, error) {
return slackConfig, nil
}

var workSpaceConfig slack.WorkSpaceConfig
if err := config.SlackConfig.Decode(&workSpaceConfig); err != nil {
return nil, fmt.Errorf("invalid slack workspace config: %w", err)
}

slackConfig = &slack.Config{
Workspaces: config.Workspaces,
Workspaces: workSpaceConfig.Workspaces,
Messages: config.Messages,
}

Expand Down
34 changes: 19 additions & 15 deletions plugins/notifiers/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ func TestNewSlackConfig(t *testing.T) {
config: &Config{
Provider: ProviderTypeSlack,
AccessToken: "foo",
Workspaces: []slack.SlackWorkspace{
{
WorkspaceName: "default",
AccessToken: "bar",
Criteria: "1==1",
SlackConfig: SlackConfig{
"workspaces": []slack.SlackWorkspace{
{
WorkspaceName: "default",
AccessToken: "bar",
Criteria: "1==1",
},
},
},
},
Expand Down Expand Up @@ -65,16 +67,18 @@ func TestNewSlackConfig(t *testing.T) {
args: args{
config: &Config{
Provider: ProviderTypeSlack,
Workspaces: []slack.SlackWorkspace{
{
WorkspaceName: "A",
AccessToken: "foo",
Criteria: "$email contains '@abc'",
},
{
WorkspaceName: "B",
AccessToken: "bar",
Criteria: "$email contains '@xyz'",
SlackConfig: SlackConfig{
"workspaces": []slack.SlackWorkspace{
{
WorkspaceName: "A",
AccessToken: "foo",
Criteria: "$email contains '@abc'",
},
{
WorkspaceName: "B",
AccessToken: "bar",
Criteria: "$email contains '@xyz'",
},
},
},
},
Expand Down
25 changes: 20 additions & 5 deletions plugins/notifiers/slack/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"github.com/goto/guardian/pkg/evaluator"
"github.com/goto/salt/log"
"html/template"
"net/http"
"net/url"
Expand Down Expand Up @@ -34,6 +35,10 @@ type userResponse struct {
Error string `json:"error"`
}

type WorkSpaceConfig struct {
Workspaces []SlackWorkspace `mapstructure:"workspaces"`
}

type SlackWorkspace struct {
WorkspaceName string `mapstructure:"workspace" validate:"required"`
AccessToken string `mapstructure:"access_token" validate:"required"`
Expand All @@ -47,6 +52,7 @@ type Notifier struct {
Messages domain.NotificationMessages
httpClient utils.HTTPClient
defaultMessageFiles embed.FS
logger *log.Logrus
}

type slackIDCacheItem struct {
Expand All @@ -62,27 +68,28 @@ type Config struct {
//go:embed templates/*
var defaultTemplates embed.FS

func NewNotifier(config *Config, httpClient utils.HTTPClient) *Notifier {
func NewNotifier(config *Config, httpClient utils.HTTPClient, logger *log.Logrus) *Notifier {
return &Notifier{
workspaces: config.Workspaces,
slackIDCache: map[string]*slackIDCacheItem{},
Messages: config.Messages,
httpClient: httpClient,
defaultMessageFiles: defaultTemplates,
logger: logger,
}
}

func (n *Notifier) Notify(items []domain.Notification) []error {
errs := make([]error, 0)
for _, item := range items {
var ws *SlackWorkspace
var slackWorkspace *SlackWorkspace
var slackID string
labelSlice := utils.MapToSlice(item.Labels)

// check cache
if n.slackIDCache[item.User] != nil {
slackID = n.slackIDCache[item.User].SlackID
ws = n.slackIDCache[item.User].Workspace
slackWorkspace = n.slackIDCache[item.User].Workspace
} else {
ws, err := n.GetSlackWorkspaceForUser(item.User)
if err != nil {
Expand All @@ -100,16 +107,24 @@ func (n *Notifier) Notify(items []domain.Notification) []error {
SlackID: slackID,
Workspace: ws,
}
slackWorkspace = ws
}

if slackWorkspace == nil {
errs = append(errs, fmt.Errorf("%v | no slack workspace found for user: %s", labelSlice, item.User))
continue
}

n.logger.Debug(fmt.Sprintf("%v | sending slack notification to user:%s in workspace:%s", labelSlice, item.User, slackWorkspace.WorkspaceName))

msg, err := ParseMessage(item.Message, n.Messages, n.defaultMessageFiles)
if err != nil {
errs = append(errs, fmt.Errorf("%v | error parsing message : %w", labelSlice, err))
continue
}

if err := n.sendMessage(*ws, slackID, msg); err != nil {
errs = append(errs, fmt.Errorf("%v | error sending message to user:%s in workspace:%s | %w", labelSlice, item.User, ws.WorkspaceName, err))
if err := n.sendMessage(*slackWorkspace, slackID, msg); err != nil {
errs = append(errs, fmt.Errorf("%v | error sending message to user:%s in workspace:%s | %w", labelSlice, item.User, slackWorkspace.WorkspaceName, err))
continue
}
}
Expand Down
2 changes: 1 addition & 1 deletion plugins/notifiers/slack/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (s *ClientTestSuite) setup() {
Messages: s.messages,
}

s.notifier = slack.NewNotifier(conf, s.mockHttpClient)
s.notifier = slack.NewNotifier(conf, s.mockHttpClient, nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
s.notifier = slack.NewNotifier(conf, s.mockHttpClient, nil)
s.notifier = slack.NewNotifier(conf, s.mockHttpClient, log.NewNoop())

}

func (s *ClientTestSuite) TestNotify() {
Expand Down
Loading