Skip to content

feat(ui): pick a Slack channel and send a test message - #12436

Draft
pfe-nazaries wants to merge 2 commits into
feature/slack-integrationfrom
feature/slack-channel-test-message
Draft

feat(ui): pick a Slack channel and send a test message#12436
pfe-nazaries wants to merge 2 commits into
feature/slack-integrationfrom
feature/slack-channel-test-message

Conversation

@pfe-nazaries

Copy link
Copy Markdown
Contributor

Context

PR 2 of 4 in the Slack integration stack. Based on #12435 (connect a workspace) — review that one first; this PR's diff is only the channel and test-message layer.

Once a workspace is connected, the tenant needs somewhere for Prowler to post, and a way to prove the delivery path actually works before anything is built on top of it.

Scope: UI only. The Slack API lives in a separate private repository, implemented by the backend lane against the shared contract.

Description

  • Channel selector (slack-channel-selector.tsx) — a self-contained, props-driven component: options carrying is_private, value, onChange, empty and error states. It knows nothing about the management page and nothing about server actions. This is deliberate: the alert-rule form will import it unchanged rather than needing a rewrite, so welding it into the manager was the thing to avoid.
  • Channel listing is the reusable primitive; the channel stored on the integration is only its default destination. A future consumer that needs a different channel per alert rule reads the same endpoint and stores its own choice, with no change to the integration's shape.
  • Listing follows JSON:API links.next cursor pagination until exhausted — Slack's conversations.list is rate-limited and paginated, and the fixtures exercise the aggregation across two pages.
  • Saving submits only channel_id; the API validates it against Slack and derives channel_name server-side, so a channel cannot be recorded blind and the stored name cannot drift from the id.
  • Private channels appear only where the Prowler app has been invited. That invite happens in Slack, by the channel's own members, and no scope bypasses it — so the picker explains it next to a refresh affordance rather than pretending it can fix it.
  • Test message reuses the existing async task-polling helpers rather than adding a synchronous path, and is offered only when a default channel is recorded.

Steps to review

  1. Read slack-channel-selector.tsx first and check its import list — if it ever reaches for a server action or manager state, the seam it exists for is gone.
  2. Target the Slack test files directly. Expect 16 passing tests in slack-page.integration.test.tsx at this layer (8 inherited, 8 added) plus 2 in the catalogue file.
  3. Worth reading as journeys: channels offered and the selection remembered; an invited private channel offered, marked private, and saved; the workspace exposing no channels; Slack refusing the listing; test message sent; test message refused.
  4. No reachable backend yet — verification against the development backend is a tracked checkpoint, not part of this PR.

Checklist

Community Checklist
  • This feature/issue is listed in the roadmap
  • Is it assigned to me
  • Are there new checks included in this PR? No
  • Review if the code is being covered by tests.
  • Review if code is being documented — the docs page ships in PR 4 of this stack.
  • Review if backport is needed. No — new feature.
  • Review if is needed to change the Readme.md No.
  • Ensure a changelog fragment is added under <component>/changelog.d/.

UI

  • All issue/task requirements work as expected on the UI — against MSW handlers. Not yet verified against a real backend.
  • Screenshots/Video - Mobile (X < 640px) — not captured.
  • Screenshots/Video - Tablet (640px > X < 1024px) — not captured.
  • Screenshots/Video - Desktop (X > 1024px) — 1920x1080 captures of the picker and a delivered test message ship in PR 4, alongside the docs page they illustrate.
  • Ensure a changelog fragment is added under ui/changelog.d/

API

Not applicable — no API change in this PR, by design.

License

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 30199519-a0e7-46d9-b2cf-8dc1ecf35327

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

Offer the connected workspace's channels — public, plus the private ones
@Prowler has been invited to — through a props-driven picker the alerts
form can import unchanged, record the choice as the integration's default
(only channel_id travels; the API derives the name), and prove delivery
with a test message that polls the task the API hands back.
Read the refusal's `code` on every Slack-backed flow, not only on the
OAuth ones: the channel listing kept the wait Slack asked for nowhere,
the channel save reported the two channel refusals as one sentence, and
the test message showed Slack's raw reason as if it were copy.

- Keep `Retry-After` on a rate-limited channel listing, so the user is
  told when to come back instead of only that it failed
- Record the destination through a Slack action, so "the channel is
  gone" and "invite @Prowler to it" stop collapsing into one message
- Map the reason the test-message task settles with, falling back to the
  API's own wording when it is not one Prowler has copy for
- Give the MSW refusals the API's real body shape, code and all
@pfe-nazaries
pfe-nazaries force-pushed the feature/slack-channel-test-message branch from a9a340d to 7778199 Compare August 13, 2026 12:33
@github-actions

Copy link
Copy Markdown
Contributor

✅ All required changelog fragments are present.

@github-actions

Copy link
Copy Markdown
Contributor

No Conflicts

No conflict markers, and the branch merges cleanly into its base.


try {
for (let page = 0; next && page < MAX_CHANNEL_PAGES; page += 1) {
const response: Response = await fetch(next, { method: "GET", headers });
Comment on lines +325 to +338
const response = await fetch(url.toString(), {
method: "PATCH",
headers,
body: JSON.stringify({
data: {
type: "integrations",
id: integrationId,
attributes: {
integration_type: "slack",
configuration: { channel_id: channelId },
},
},
}),
});
);

try {
const response = await fetch(url.toString(), { method: "POST", headers });
@github-actions

Copy link
Copy Markdown
Contributor

🔒 Container Security Scan

Image: prowler-ui:91c2425
Last scan: 2026-08-13 12:38:24 UTC

✅ No Vulnerabilities Detected

The container image passed all security checks. No known CVEs were found.

📋 Resources:

@github-actions

Copy link
Copy Markdown
Contributor

🔎 Container Security Scan (Grype)

Image: prowler-ui:91c2425
Last scan: 2026-08-13 12:39:42 UTC

✅ Nothing Blocking

No findings at critical or high severity.


📋 Resources:

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants