Skip to content

refactor(slack): move configuration types into slack package#5239

Open
santiagofn wants to merge 1 commit into
prometheus:mainfrom
santiagofn:refactor/slack-move-config
Open

refactor(slack): move configuration types into slack package#5239
santiagofn wants to merge 1 commit into
prometheus:mainfrom
santiagofn:refactor/slack-move-config

Conversation

@santiagofn
Copy link
Copy Markdown
Contributor

@santiagofn santiagofn commented May 14, 2026

Part 2 of a series implementing threaded message support for the Slack notifier. Full picture (it might be outdated): #5150.

Summary

Moves SlackConfig and related types from config/notifiers.go into a new notify/slack/config.go, following the same pattern used recently for the mattermost, jira, and incidentio notifiers.

The Slack-specific unmarshal tests are moved alongside them into notify/slack/config_test.go. config.Receiver now refers to *slack.Config, and notify/slack no longer needs to import the top-level config package.

Naming

While moving the types, the Slack prefix is dropped from each:

Before After
SlackConfig Config
SlackAction Action
SlackField Field
SlackConfirmationField ConfirmationField
DefaultSlackConfig DefaultConfig

This follows the Go team's guidance against stuttering identifiers, so call sites read slack.Config instead of slack.SlackConfig:

Extra changes driven by CodeRabbit review

  • Fix nil-pointer panic in Config.UnmarshalYAML: guard c.APIURL == nil before calling c.APIURL.String() in the update_message validation. Previously update_message: true without api_url panicked instead of returning the validation error the message string already promised.
  • Regression test: covers update_message: true with no api_url in notify/slack/config_test.go.
  • Write back defaulted slack.Config to rcv.SlackConfigs: when a receiver had a nil entry in slack_configs (e.g. - ~ in YAML), Config.UnmarshalYAML rebound sc to a fresh &slack.Config{} locally but never assigned it back to the slice; the nil entry then flowed into BuildReceiverIntegrationsslack.New, which dereferenced c.HTTPConfig and panicked. Now the new Config is written back to rcv.SlackConfigs[i].

No behavioral changes apart from the panic-to-error fixes.

Made with Cursor

Summary by CodeRabbit

  • New Features
    • Enhanced Slack notifier configuration: interactive message components support, token/API URL/file options, and stricter validation rules for auth and update-message usage.
  • Refactor
    • Reorganized Slack notifier internals to use a consolidated, package-local configuration model.
  • Tests
    • Expanded Slack config validation tests and updated notifier tests to reflect new configuration semantics.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

📝 Walkthrough

Walkthrough

Extracts Slack notifier configuration into a new notify/slack package (types, defaults, and validation), updates the Slack notifier implementation and tests to use the new local Config/Field/Action types, and removes the deprecated Slack types from the global config package.

Changes

Slack Configuration Module Migration

Layer / File(s) Summary
Define new Slack configuration module with types and defaults
notify/slack/config.go
Config, Action, ConfirmationField, and Field types are introduced with YAML unmarshalling, DefaultConfig, and validation enforcing required fields and auth/endpoint constraints.
Test Slack configuration parsing and validation
notify/slack/config_test.go
Adds strict YAML tests for Slack config loading, auth-method validation, field/action validation, pointer boolean semantics, and update_message constraints.
Update Slack notifier implementation to use local Config type
notify/slack/slack.go, notify/slack/types.go
Notifier constructor and internal attachment types now use the local *Config, Field, Action, and ConfirmationField types; removes config package import from slack.go.
Update Slack notifier tests to use local Config
notify/slack/slack_test.go
Notifier tests updated to instantiate with local &Config{} instances across retry, URL handling, timeout, notify-with-reason, and message field tests.
Remove Slack types from config package and update Receiver
config/config.go, config/notifiers.go
Deletes global Slack types and DefaultSlackConfig, updates Receiver.SlackConfigs to []*slack.Config, and initializes nil Slack config entries to &slack.Config{}.
Update config notifier tests
config/notifiers_test.go
Replaces removed Slack tests with Opsgenie, SNS, WeChat, Webex, Telegram, and Email YAML unmarshalling/validation tests.

Sequence Diagram(s)

sequenceDiagram
  participant Receiver
  participant notify_slack_New as notify/slack.New
  participant SlackAPI as Slack API
  Receiver->>notify_slack_New: passes *slack.Config (receiver.SlackConfigs)
  notify_slack_New->>SlackAPI: sends rendered message using Config (APIURL / token)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • TheMeier
  • SoloJacobs
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: moving Slack configuration types into the slack package as part of a refactoring effort.
Description check ✅ Passed The description provides comprehensive context including PR series reference, clear summary of changes, explicit naming justification with before/after table and Go guidance citations, and details of CodeRabbit-driven panic fixes with regression tests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@notify/slack/config_test.go`:
- Around line 92-137: Update TestSlackAuthMethodConfigValidation to include a
new test case where the YAML input contains "update_message: true" but omits
"api_url" (e.g. in: "`update_message: true\n`"). Use the same table-driven
structure (tests slice) and set expectedErr to the validation error string
produced by Config's strict unmarshal/validation for missing api_url when
update_message is true; run yaml.UnmarshalStrict into a Config and assert that
err is non-nil and matches that expectedErr (ensuring it does not panic).
Reference the existing test function TestSlackAuthMethodConfigValidation, the
Config type, update_message, api_url, and yaml.UnmarshalStrict so the new case
follows the same pattern as other entries.

In `@notify/slack/config.go`:
- Around line 190-191: The validation uses c.APIURL.String() without ensuring
c.APIURL is non-nil which can panic; update the check in the validation (where
c.UpdateMessage is inspected) to first guard c.APIURL (e.g., if c.UpdateMessage
{ if c.APIURL == nil { return errors.New("update_message requires api_url to be
set to https://slack.com/api/chat.postMessage") } if c.APIURL.String() !=
"https://slack.com/api/chat.postMessage" { return errors.New("update_message can
only be used with bot tokens. api_url must be set to
https://slack.com/api/chat.postMessage") } }) so you return a config error
instead of dereferencing a nil pointer (referencing c.UpdateMessage, c.APIURL
and APIURL.String()).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: e6baa8e2-ad66-4324-8620-7c045c901d1b

📥 Commits

Reviewing files that changed from the base of the PR and between 36afed4 and f559570.

📒 Files selected for processing (8)
  • config/config.go
  • config/notifiers.go
  • config/notifiers_test.go
  • notify/slack/config.go
  • notify/slack/config_test.go
  • notify/slack/slack.go
  • notify/slack/slack_test.go
  • notify/slack/types.go
💤 Files with no reviewable changes (2)
  • config/notifiers_test.go
  • config/notifiers.go

Comment thread notify/slack/config_test.go
Comment thread notify/slack/config.go Outdated
@santiagofn santiagofn force-pushed the refactor/slack-move-config branch from f559570 to b70e44b Compare May 26, 2026 15:39
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@config/config.go`:
- Around line 414-417: Loop variable sc is being defaulted but not written back
into the slice, leaving nil entries in rcv.SlackConfigs that later cause a panic
in BuildReceiverIntegrations -> notify/slack.New; change the loop that iterates
rcv.SlackConfigs to assign the defaulted value back into the slice (e.g., set
rcv.SlackConfigs[i] = &slack.Config{} when sc == nil) so the slice contains
non-nil *slack.Config elements before they are passed to notify/slack.New.

In `@notify/slack/config_test.go`:
- Around line 238-255: Add explicit length assertions before iterating
element-wise so missing/unmarshalled entries fail the test: check
len(cfg.Fields) == len(expected) (and the analogous slices at the second
location covering lines 302-338) and call t.Fatalf or t.Fatalf-style assertion
when they differ, then proceed with the existing index-based loops; reference
the cfg.Fields and expected slices used in the for index, field := range
cfg.Fields loops to locate both spots to update.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 7a186bfb-2f89-4705-8929-ef2597921e38

📥 Commits

Reviewing files that changed from the base of the PR and between f559570 and b70e44b.

📒 Files selected for processing (8)
  • config/config.go
  • config/notifiers.go
  • config/notifiers_test.go
  • notify/slack/config.go
  • notify/slack/config_test.go
  • notify/slack/slack.go
  • notify/slack/slack_test.go
  • notify/slack/types.go
💤 Files with no reviewable changes (2)
  • config/notifiers.go
  • config/notifiers_test.go

Comment thread config/config.go Outdated
Comment on lines +238 to +255
for index, field := range cfg.Fields {
exp := expected[index]
if field.Title != exp.Title {
t.Errorf("\nexpected:\n%v\ngot:\n%v", exp.Title, field.Title)
}
if field.Value != exp.Value {
t.Errorf("\nexpected:\n%v\ngot:\n%v", exp.Value, field.Value)
}
if exp.Short == nil && field.Short != nil {
t.Errorf("\nexpected:\n%v\ngot:\n%v", exp.Short, *field.Short)
}
if exp.Short != nil && field.Short == nil {
t.Errorf("\nexpected:\n%v\ngot:\n%v", *exp.Short, field.Short)
}
if exp.Short != nil && *exp.Short != *field.Short {
t.Errorf("\nexpected:\n%v\ngot:\n%v", *exp.Short, *field.Short)
}
}
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 26, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Assert slice lengths before element-wise checks.

At Line 238 and Line 302, both loops only iterate over parsed output. If unmarshalling drops entries, these tests can pass without detecting missing items.

Proposed fix
 func TestFieldConfigUnmarshaling(t *testing.T) {
@@
-	for index, field := range cfg.Fields {
+	if len(cfg.Fields) != len(expected) {
+		t.Fatalf("\nexpected:\n%d\ngot:\n%d", len(expected), len(cfg.Fields))
+	}
+	for index, field := range cfg.Fields {
 		exp := expected[index]
@@
 func TestActionsValidation(t *testing.T) {
@@
-	for index, action := range cfg.Actions {
+	if len(cfg.Actions) != len(expected) {
+		t.Fatalf("\nexpected:\n%d\ngot:\n%d", len(expected), len(cfg.Actions))
+	}
+	for index, action := range cfg.Actions {
 		exp := expected[index]

Also applies to: 302-338

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@notify/slack/config_test.go` around lines 238 - 255, Add explicit length
assertions before iterating element-wise so missing/unmarshalled entries fail
the test: check len(cfg.Fields) == len(expected) (and the analogous slices at
the second location covering lines 302-338) and call t.Fatalf or t.Fatalf-style
assertion when they differ, then proceed with the existing index-based loops;
reference the cfg.Fields and expected slices used in the for index, field :=
range cfg.Fields loops to locate both spots to update.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this is a pre-existing pattern that comes from config/notifiers_test.go. the risk (silent unmarshal entry-drop) is low for these tests: the inputs are short YAMLs, not user data. so i'll leave it as it is.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@santiagofn, understood — hardcoded YAML literals carry negligible risk of silent entry-drops, and keeping the pattern consistent with the original config/notifiers_test.go is a reasonable call. Thanks for the context!

(ˆ·ˆ) 🐇

Moves SlackConfig (and the related SlackAction, SlackConfirmationField,
SlackField types plus DefaultSlackConfig) from config/notifiers.go into
a new notify/slack/config.go, following the same pattern used recently
for the mattermost, jira, and incidentio notifiers.

While moving them, the Slack prefix is dropped per the package-naming
guidance in https://go.dev/blog/package-names ("Avoid repetition"):
SlackConfig -> Config, DefaultSlackConfig -> DefaultConfig, SlackAction
-> Action, SlackConfirmationField -> ConfirmationField, SlackField ->
Field. External callers now refer to slack.Config etc., reading better
than the previous slack.SlackConfig stutter.

The Slack-specific unmarshal tests are moved alongside them into
notify/slack/config_test.go. config.Receiver now refers to
*slack.Config, and notify/slack no longer needs to import the top-level
config package.

No behavioral changes.

Signed-off-by: Santiago Fernández Núñez <santiago.nunez@nubank.com.br>
Co-authored-by: Cursor <cursoragent@cursor.com>
@santiagofn santiagofn force-pushed the refactor/slack-move-config branch from b70e44b to 14a0060 Compare May 26, 2026 15:55
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
notify/slack/config_test.go (2)

238-255: ⚡ Quick win

Assert slice length before element-wise comparison.

The loop iterates over cfg.Fields and accesses expected[index]. If unmarshalling silently drops entries, the test would pass without detecting missing items.

Proposed fix
+	if len(cfg.Fields) != len(expected) {
+		t.Fatalf("expected %d fields, got %d", len(expected), len(cfg.Fields))
+	}
 	for index, field := range cfg.Fields {
 		exp := expected[index]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@notify/slack/config_test.go` around lines 238 - 255, The test iterates
cfg.Fields and indexes into expected without checking lengths, so missing or
dropped entries can be ignored; before the element-wise loop in the test (the
block that iterates "for index, field := range cfg.Fields"), add an assertion
that len(cfg.Fields) == len(expected) (using t.Fatalf or t.Fatalf-like failure)
to fail fast on mismatched slice lengths, and only then proceed with the
existing per-index comparisons of cfg.Fields and expected.

302-338: ⚡ Quick win

Assert slice length before element-wise comparison.

Same issue as TestFieldConfigUnmarshaling: if unmarshalling drops action entries, the test would silently pass without detecting the missing items.

Proposed fix
+	if len(cfg.Actions) != len(expected) {
+		t.Fatalf("expected %d actions, got %d", len(expected), len(cfg.Actions))
+	}
 	for index, action := range cfg.Actions {
 		exp := expected[index]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@notify/slack/config_test.go` around lines 302 - 338, The test iterates
cfg.Actions and compares elements to expected but never asserts that
len(cfg.Actions) == len(expected), so missing or extra actions would be
undetected; add an explicit length assertion before the loop (compare
len(cfg.Actions) and len(expected)) in the test that uses cfg.Actions and
expected (same pattern as in TestFieldConfigUnmarshaling) so the test fails when
unmarshalling drops or adds action entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@notify/slack/config_test.go`:
- Around line 238-255: The test iterates cfg.Fields and indexes into expected
without checking lengths, so missing or dropped entries can be ignored; before
the element-wise loop in the test (the block that iterates "for index, field :=
range cfg.Fields"), add an assertion that len(cfg.Fields) == len(expected)
(using t.Fatalf or t.Fatalf-like failure) to fail fast on mismatched slice
lengths, and only then proceed with the existing per-index comparisons of
cfg.Fields and expected.
- Around line 302-338: The test iterates cfg.Actions and compares elements to
expected but never asserts that len(cfg.Actions) == len(expected), so missing or
extra actions would be undetected; add an explicit length assertion before the
loop (compare len(cfg.Actions) and len(expected)) in the test that uses
cfg.Actions and expected (same pattern as in TestFieldConfigUnmarshaling) so the
test fails when unmarshalling drops or adds action entries.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b0ebc29b-6dbd-439e-941a-685a02980bf4

📥 Commits

Reviewing files that changed from the base of the PR and between b70e44b and 14a0060.

📒 Files selected for processing (8)
  • config/config.go
  • config/notifiers.go
  • config/notifiers_test.go
  • notify/slack/config.go
  • notify/slack/config_test.go
  • notify/slack/slack.go
  • notify/slack/slack_test.go
  • notify/slack/types.go
💤 Files with no reviewable changes (2)
  • config/notifiers_test.go
  • config/notifiers.go
✅ Files skipped from review due to trivial changes (1)
  • notify/slack/slack_test.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant