Skip to content

Conversation

myftija
Copy link
Member

@myftija myftija commented Apr 23, 2025

This PR fixes a bug which broke the creation of new alerts when an existing Slack integration is expired/revoked by the user. Also, transient errors when listing slack channels are now handled gracefully.

An approach to handle expired tokens automatically might still be needed; remains for a separate PR.

Closes #1414

Screenshots

A note about the token revoked issue is now shown the user alongside a button to reconnect the slack integration.
image

Summary by CodeRabbit

  • New Features

    • Improved Slack integration error handling with clear user-facing messages for revoked or expired access and failed channel loading.
    • Added UI prompts to reconnect Slack workspace if access is revoked or expired, including a direct "Connect to Slack" button.
  • Bug Fixes

    • Enhanced reliability and clarity when fetching Slack channels or handling integration errors.
  • Chores

    • Removed unused imports to streamline the codebase.

Copy link

changeset-bot bot commented Apr 23, 2025

⚠️ No Changeset found

Latest commit: 24d23c9

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

coderabbitai bot commented Apr 23, 2025

Walkthrough

The changes implement robust error handling for Slack integration within the alert channel setup flow. The Slack channel fetch logic now detects revoked tokens and other Slack API errors, returning specific statuses such as "TOKEN_REVOKED," "TOKEN_EXPIRED," and "FAILED_FETCHING_CHANNELS." The UI is updated to recognize these new statuses, displaying appropriate callouts and offering a Slack reconnection option if access is revoked or expired. Additionally, the Slack connection route now supports a "reinstall" query parameter to facilitate reconnection. Unused imports are removed from relevant files for clarity.

Changes

File(s) Change Summary
apps/webapp/app/presenters/v3/NewAlertChannelPresenter.server.ts Enhanced error handling for Slack channel fetching; added detection for revoked and expired tokens and Slack API errors; refactored fallback logic; added Slack error type guard.
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new.connect-to-slack.ts Added "reinstall" query param handling for Slack reconnection; simplified redirect logic; removed unused imports.
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx Added UI states for "TOKEN_REVOKED," "TOKEN_EXPIRED," and "FAILED_FETCHING_CHANNELS" Slack statuses; improved user guidance and reconnection flow.
apps/webapp/app/v3/services/createOrgIntegration.server.ts Removed unused imports for clarity; no logic changes.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI
    participant Server
    participant Slack

    User->>UI: Open New Alert Modal
    UI->>Server: Fetch Slack Channels
    Server->>Slack: API call to fetch channels
    alt Token revoked or expired
        Slack-->>Server: Error (token_revoked or token_expired)
        Server-->>UI: Status "TOKEN_REVOKED" or "TOKEN_EXPIRED"
        UI-->>User: Show callout, offer reconnect button
        User->>UI: Click "Connect to Slack"
        UI->>Server: Redirect to Slack auth with reinstall param
    else Other Slack error
        Slack-->>Server: Error (other)
        Server-->>UI: Status "FAILED_FETCHING_CHANNELS"
        UI-->>User: Show warning callout
    else Success
        Slack-->>Server: Channels list
        Server-->>UI: Show channels
    end
Loading

Assessment against linked issues

Objective Addressed Explanation
Handle revoked/expired Slack connections gracefully in the new alerts modal (#1414, TRI-3871)
Allow users to reconnect Slack if access is revoked (#1414, TRI-3871)
Prevent modal from erroring out and blocking alert creation if Slack is revoked (#1414, TRI-3871)

Suggested reviewers

  • matt-aitken

Poem

A rabbit hopped through code so neat,
To mend the Slack alert defeat.
If tokens revoked, don’t feel blue—
A callout and button now guide you through!
With errors handled, flows refined,
Alerts and Slack are re-aligned.
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b8f4fba and 24d23c9.

📒 Files selected for processing (2)
  • apps/webapp/app/presenters/v3/NewAlertChannelPresenter.server.ts (3 hunks)
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/webapp/app/presenters/v3/NewAlertChannelPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: typecheck / typecheck

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@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: 1

🧹 Nitpick comments (5)
apps/webapp/app/presenters/v3/NewAlertChannelPresenter.server.ts (1)

120-131: isSlackError can be simplified & made type‑safer

  1. Use a type predicate that narrows on SlackAPIError rather than a bespoke shape to prevent false positives.
  2. Rely on in operator only once after confirming the object is not null.
-function isSlackError(obj: unknown): obj is { data: { error: string } } {
-  return Boolean(
-    typeof obj === "object" &&
-      obj !== null &&
-      "data" in obj &&
-      typeof obj.data === "object" &&
-      obj.data !== null &&
-      "error" in obj.data &&
-      typeof obj.data.error === "string"
-  );
+interface SlackAPIError {
+  data: { error: string };
+}
+
+function isSlackError(obj: unknown): obj is SlackAPIError {
+  if (typeof obj !== "object" || obj === null) return false;
+  const maybe = obj as Record<string, unknown>;
+  const data = maybe["data"];
+  return (
+    typeof data === "object" &&
+    data !== null &&
+    typeof (data as Record<string, unknown>)["error"] === "string"
+  );
 }

This keeps the guard concise and easier to maintain.

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new.connect-to-slack.ts (2)

34-43: Success message may confuse users when no (re)installation occurred

When an integration already exists and reinstall is not requested, the loader redirects with
"Successfully connected your Slack workspace" even though no OAuth round‑trip happened in this request.
Consider wording such as “Slack workspace already connected” to avoid misleading feedback.


45-53: Avoid awaiting a function that already returns a Response

OrgIntegrationRepository.redirectToAuthService(...) already returns a Response.
The surrounding await is unnecessary and can be dropped, saving a tick and making intent clearer.

-  return await OrgIntegrationRepository.redirectToAuthService(
+  return OrgIntegrationRepository.redirectToAuthService(
     "SLACK",
     project.organizationId,
     request,
     v3NewProjectAlertPathConnectToSlackPath({ slug: organizationSlug }, project, {
       slug: envParam,
     })
   );
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx (2)

359-378: Maintain layout consistency by re‑using InputGroup wrapper

For the "ACCESS_REVOKED" branch we fall back to a raw <div> whereas the other branches stay inside an InputGroup.
This can lead to inconsistent spacing/margins compared to the rest of the form.

-                ) : slack.status === "ACCESS_REVOKED" ? (
-                  <div className="flex flex-col gap-4">
+                ) : slack.status === "ACCESS_REVOKED" ? (
+                  <InputGroup fullWidth className="flex flex-col gap-4">
     ...
-                  </div>
+                  </InputGroup>

Keeps vertical rhythm identical across all states.


379-383: Allow quick retry after transient Slack errors

In the "FAILED_FETCHING_CHANNELS" state we only show a Callout without any action.
Providing a lightweight “Retry” button could improve UX for transient outages.

) : slack.status === "FAILED_FETCHING_CHANNELS" ? (
  <div className="flex flex-col gap-4">
    <Callout variant="warning">
      Failed loading channels from Slack. Please try again later.
    </Callout>
+   <Button
+     variant="tertiary/medium"
+     onClick={() => navigate(0)} // full page reload
+   >
+     Retry
+   </Button>
  </div>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26aad58 and b8f4fba.

📒 Files selected for processing (4)
  • apps/webapp/app/presenters/v3/NewAlertChannelPresenter.server.ts (3 hunks)
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new.connect-to-slack.ts (2 hunks)
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx (1 hunks)
  • apps/webapp/app/v3/services/createOrgIntegration.server.ts (0 hunks)
💤 Files with no reviewable changes (1)
  • apps/webapp/app/v3/services/createOrgIntegration.server.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/webapp/app/presenters/v3/NewAlertChannelPresenter.server.ts (1)
apps/webapp/app/models/orgIntegration.server.ts (1)
  • OrgIntegrationRepository (50-244)
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: typecheck / typecheck

Comment on lines 34 to 55
const [error, channels] = await tryCatch(getSlackChannelsForToken(slackIntegration));

if (error) {
if (isSlackError(error) && error.data.error === "token_revoked") {
return {
slack: {
status: "ACCESS_REVOKED" as const,
},
};
}

logger.error("Failed fetching Slack channels for creating alerts", {
error,
slackIntegrationId: slackIntegration.id,
});

return {
slack: {
status: "FAILED_FETCHING_CHANNELS" as const,
},
};
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

token_revoked scenario will never be detected with the current error propagation

getAllSlackConversations() converts any Slack‑API error into a plain Error (see lines 110‑113) thereby discarding the original data.error payload that isSlackError() relies on.
As a result, the "ACCESS_REVOKED" path is unreachable and users with a revoked token will instead fall into "FAILED_FETCHING_CHANNELS".

-  if (!response.ok) {
-    throw new Error(`Failed to get channels: ${response.error}`);
+  if (!response.ok) {
+    // Preserve Slack error semantics so the presenter can react accurately
+    const err: { data: { error: string } } = {
+      data: { error: response.error ?? "unknown_error" },
+    };
+    throw err;
   }

Alternatively, parse error.message when it contains "token_revoked" inside the presenter, but preserving the structured object keeps concerns better separated.

Committable suggestion skipped: line range outside the PR's diff.

@ericallam ericallam merged commit 2dbc085 into triggerdotdev:main Apr 23, 2025
6 of 7 checks passed
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.

[TRI-3871] The new alerts modal doesn't work if you have a Slack connection that is revoked/expired

2 participants