From a555a5e29ea4cd4e0003af76709f0967b549fbc3 Mon Sep 17 00:00:00 2001 From: Brian Ojeda <9335829+sgtoj@users.noreply.github.com> Date: Tue, 9 Dec 2025 11:08:49 -0500 Subject: [PATCH] feat: add optional footer note for pc compliance --- .env.example | 2 ++ internal/app/app.go | 5 ++++- internal/config/config.go | 4 ++++ internal/notifiers/slack.go | 14 +++++++++++--- internal/notifiers/slack_messages.go | 7 +++++++ 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index 5c76fbf..d2a7821 100644 --- a/.env.example +++ b/.env.example @@ -30,6 +30,8 @@ APP_SLACK_CHANNEL=C01234ABCDE # APP_SLACK_CHANNEL_PR_BYPASS=C01234ABCDE # APP_SLACK_CHANNEL_OKTA_SYNC=C01234ABCDE # APP_SLACK_CHANNEL_ORPHANED_USERS=C01234ABCDE +# optional: custom footer note for PR bypass notifications (supports Slack mrkdwn) +# APP_SLACK_FOOTER_NOTE_PR_BYPASS=_Please review the ._ # api gateway base path (optional, for lambda deployments with stage prefix) # APP_BASE_PATH=v1 diff --git a/internal/app/app.go b/internal/app/app.go index 57ff04b..8d6516b 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -72,7 +72,10 @@ func New(ctx context.Context, cfg *config.Config) (*App, error) { OktaSync: cfg.SlackChannelOktaSync, OrphanedUsers: cfg.SlackChannelOrphanedUsers, } - app.Notifier = notifiers.NewSlackNotifierWithAPIURL(cfg.SlackToken, channels, cfg.SlackAPIURL) + messages := notifiers.SlackMessages{ + PRBypassFooterNote: cfg.SlackPRBypassFooterNote, + } + app.Notifier = notifiers.NewSlackNotifierWithAPIURL(cfg.SlackToken, channels, messages, cfg.SlackAPIURL) } return app, nil diff --git a/internal/config/config.go b/internal/config/config.go index c088026..5249c7d 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -56,6 +56,7 @@ type Config struct { SlackChannelPRBypass string SlackChannelOktaSync string SlackChannelOrphanedUsers string + SlackPRBypassFooterNote string SlackAPIURL string } @@ -179,6 +180,7 @@ func NewConfigWithContext(ctx context.Context) (*Config, error) { SlackChannelPRBypass: os.Getenv("APP_SLACK_CHANNEL_PR_BYPASS"), SlackChannelOktaSync: os.Getenv("APP_SLACK_CHANNEL_OKTA_SYNC"), SlackChannelOrphanedUsers: os.Getenv("APP_SLACK_CHANNEL_ORPHANED_USERS"), + SlackPRBypassFooterNote: os.Getenv("APP_SLACK_FOOTER_NOTE_PR_BYPASS"), SlackAPIURL: os.Getenv("APP_SLACK_API_URL"), } @@ -371,6 +373,7 @@ type RedactedConfig struct { SlackChannelPRBypass string `json:"slack_channel_pr_bypass"` SlackChannelOktaSync string `json:"slack_channel_okta_sync"` SlackChannelOrphanedUsers string `json:"slack_channel_orphaned_users"` + SlackPRBypassFooterNote string `json:"slack_pr_bypass_footer_note"` SlackAPIURL string `json:"slack_api_url"` } @@ -426,6 +429,7 @@ func (c *Config) Redacted() RedactedConfig { SlackChannelPRBypass: c.SlackChannelPRBypass, SlackChannelOktaSync: c.SlackChannelOktaSync, SlackChannelOrphanedUsers: c.SlackChannelOrphanedUsers, + SlackPRBypassFooterNote: c.SlackPRBypassFooterNote, SlackAPIURL: c.SlackAPIURL, } } diff --git a/internal/notifiers/slack.go b/internal/notifiers/slack.go index 0cca41b..788e610 100644 --- a/internal/notifiers/slack.go +++ b/internal/notifiers/slack.go @@ -14,20 +14,27 @@ type SlackChannels struct { OrphanedUsers string } +// SlackMessages holds optional custom messages for different notification +// types. empty values are excluded from the notification. +type SlackMessages struct { + PRBypassFooterNote string +} + // SlackNotifier sends formatted messages to Slack channels. type SlackNotifier struct { client *slack.Client channels SlackChannels + messages SlackMessages } // NewSlackNotifier creates a Slack notifier with default API URL. -func NewSlackNotifier(token string, channels SlackChannels) *SlackNotifier { - return NewSlackNotifierWithAPIURL(token, channels, "") +func NewSlackNotifier(token string, channels SlackChannels, messages SlackMessages) *SlackNotifier { + return NewSlackNotifierWithAPIURL(token, channels, messages, "") } // NewSlackNotifierWithAPIURL creates a Slack notifier with custom API URL. // useful for testing with mock servers. -func NewSlackNotifierWithAPIURL(token string, channels SlackChannels, apiURL string) *SlackNotifier { +func NewSlackNotifierWithAPIURL(token string, channels SlackChannels, messages SlackMessages, apiURL string) *SlackNotifier { var opts []slack.Option if apiURL != "" { opts = append(opts, slack.OptionAPIURL(apiURL)) @@ -35,6 +42,7 @@ func NewSlackNotifierWithAPIURL(token string, channels SlackChannels, apiURL str return &SlackNotifier{ client: slack.New(token, opts...), channels: channels, + messages: messages, } } diff --git a/internal/notifiers/slack_messages.go b/internal/notifiers/slack_messages.go index 64b7b19..256754d 100644 --- a/internal/notifiers/slack_messages.go +++ b/internal/notifiers/slack_messages.go @@ -67,6 +67,13 @@ func (s *SlackNotifier) NotifyPRBypass(ctx context.Context, result *client.PRCom )) } + if s.messages.PRBypassFooterNote != "" { + blocks = append(blocks, slack.NewContextBlock( + "footer", + slack.NewTextBlockObject("mrkdwn", s.messages.PRBypassFooterNote, false, false), + )) + } + channel := s.channelFor(s.channels.PRBypass) _, _, err := s.client.PostMessageContext( ctx,