Skip to content

Slack integration management and team sorting improvements#333

Open
spencerhcheng wants to merge 6 commits intomainfrom
feature/slack-org-management
Open

Slack integration management and team sorting improvements#333
spencerhcheng wants to merge 6 commits intomainfrom
feature/slack-org-management

Conversation

@spencerhcheng
Copy link
Collaborator

Changes

  • Allow users without organization to manage Slack integration
  • Personal Slack integration is automatically deleted when user joins organization
  • Smooth toggle button with purple styling on management page
  • Clickable column headers for sorting (Name, Email, On-Call Status)
  • Sort indicators with arrow icons

…g join

- Users without organization_id can connect/disconnect Slack
- Personal Slack integration is deleted when user joins an organization
- Admins and super_admins can always manage Slack integration
@vercel
Copy link

vercel bot commented Feb 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
rootly-burnout-detector-dev Ready Ready Preview, Comment Feb 7, 2026 0:02am
rootly-burnout-detector-web Ready Ready Preview, Comment Feb 7, 2026 0:02am

Request Review

@claude
Copy link

claude bot commented Feb 6, 2026

Claude finished @spencerhcheng's task —— View job


PR Code Review

Tasks:

  • Read repository guidelines (CLAUDE.md) - No CLAUDE.md found
  • Analyze backend changes in invitations.py
  • Analyze frontend changes in UnifiedSlackCard.tsx
  • Report findings

Issues Found

Backend (invitations.py)

Missing Import - Line 309 & 402:

logger.info(f"Deleted personal Slack integration for user {current_user.email} joining org {invitation.organization_id}")
  • Issue: logger is used but not imported
  • Fix: Add import at top of file: import logging and logger = logging.getLogger(__name__)

Database Transaction Issue - Lines 300-322 & 392-442:

  • Issue: The Slack integration deletion happens inside the try block but before other database operations. If the subsequent operations fail, the Slack integration deletion will be rolled back, potentially leaving the user in an inconsistent state.
  • Potential Problem: User joins org but keeps personal Slack integration due to rollback
  • Recommendation: Consider the order of operations or use nested transactions

Frontend (UnifiedSlackCard.tsx)

Permission Logic Inconsistency - Lines 293-295 & 361-363:

const canDisconnect = userInfo?.role === 'admin' ||
                     userInfo?.role === 'super_admin' ||
                     !userInfo?.organization_id
  • Issue: Users without an organization can disconnect/connect Slack integrations, but the error message says "Only admins can disconnect/connect Slack integration"
  • Inconsistency: The logic allows non-org users but the error message implies only admins can perform the action
  • Fix: Update error messages to accurately reflect the permission logic

- Button now only renders for admins, super_admins, or users without org
- Members in org see message instead of button
- Prevents confusion for users who can't connect Slack
@claude
Copy link

claude bot commented Feb 6, 2026

Claude finished @spencerhcheng's task —— View job


PR Code Review

Tasks:

  • Read repository guidelines (CLAUDE.md) - No CLAUDE.md found
  • Analyze backend changes in invitations.py
  • Analyze frontend changes in UnifiedSlackCard.tsx
  • Report findings

Issues Found

Backend (invitations.py)

Missing Import - Lines 309 & 402:

logger.info(f"Deleted personal Slack integration for user {current_user.email} joining org {invitation.organization_id}")
  • Issue: logger is used but not imported
  • Fix: Add import at top of file: import logging and logger = logging.getLogger(__name__)

Database Transaction Issue - Lines 300-322 & 392-442:

  • Issue: The Slack integration deletion happens inside the try block but before other database operations. If the subsequent operations fail, the Slack integration deletion will be rolled back, potentially leaving the user in an inconsistent state.
  • Potential Problem: User joins org but keeps personal Slack integration due to rollback
  • Recommendation: Consider the order of operations or use nested transactions

Frontend (UnifiedSlackCard.tsx)

Permission Logic Inconsistency - Lines 293-295 & 357-359:

const canDisconnect = userInfo?.role === 'admin' ||
                     userInfo?.role === 'super_admin' ||
                     !userInfo?.organization_id
  • Issue: Users without an organization can disconnect/connect Slack integrations, but the error message says "Only admins can disconnect/connect Slack integration"
  • Inconsistency: The logic allows non-org users but the error message implies only admins can perform the action
  • Fix: Update error messages to accurately reflect the permission logic

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 6, 2026

Greptile Overview

Greptile Summary

This PR implements Slack integration management for users without organizations and automatic cleanup when users join organizations. However, there are critical issues that will prevent the code from running:

Major Issues:

  • Backend code references SlackIntegration.organization_id column that doesn't exist in the database model, causing AttributeError at runtime
  • Missing logger import in invitations.py will cause NameError

What Works:

  • Frontend permission checks correctly updated to allow users without organization_id to manage Slack
  • Logic for checking admin/super_admin roles is consistent across connect and disconnect flows

Required Fixes:

  1. Add database migration to add organization_id column to slack_integrations table
  2. Update SlackIntegration model at backend/app/models/slack_integration.py
  3. Add logging import and logger initialization to invitations.py

Confidence Score: 0/5

  • This PR will fail immediately at runtime due to missing database column and import
  • Critical bugs prevent this code from executing: SlackIntegration.organization_id column doesn't exist in the database model (will cause AttributeError), and logger is not imported (will cause NameError). These are blocking issues that must be resolved before merging
  • Pay close attention to backend/app/api/endpoints/invitations.py - it has critical bugs that will cause runtime failures

Important Files Changed

Filename Overview
backend/app/api/endpoints/invitations.py Added Slack integration cleanup on org join, but references non-existent organization_id column and missing logger import will cause runtime errors
frontend/src/app/integrations/components/UnifiedSlackCard.tsx Updated permission checks to allow users without organization to connect/disconnect Slack - logic looks correct

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 302 to 309
from app.models.slack import SlackIntegration
personal_slack = db.query(SlackIntegration).filter(
SlackIntegration.user_id == current_user.id,
SlackIntegration.organization_id.is_(None)
).first()
if personal_slack:
db.delete(personal_slack)
logger.info(f"Deleted personal Slack integration for user {current_user.email} joining org {invitation.organization_id}")
Copy link
Contributor

Choose a reason for hiding this comment

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

SlackIntegration model does not have an organization_id column. This query will fail at runtime with AttributeError.

Check the model at backend/app/models/slack_integration.py - it only has: id, user_id, slack_token, slack_user_id, workspace_id, webhook_url, token_source, created_at, updated_at.

If you need to track organization-level vs personal Slack integrations, you'll need to:

  1. Add a migration to add organization_id column to slack_integrations table
  2. Update the SlackIntegration model to include this column
Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/app/api/endpoints/invitations.py
Line: 302:309

Comment:
`SlackIntegration` model does not have an `organization_id` column. This query will fail at runtime with `AttributeError`.

Check the model at `backend/app/models/slack_integration.py` - it only has: `id`, `user_id`, `slack_token`, `slack_user_id`, `workspace_id`, `webhook_url`, `token_source`, `created_at`, `updated_at`.

If you need to track organization-level vs personal Slack integrations, you'll need to:
1. Add a migration to add `organization_id` column to `slack_integrations` table
2. Update the `SlackIntegration` model to include this column

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 395 to 402
from app.models.slack import SlackIntegration
personal_slack = db.query(SlackIntegration).filter(
SlackIntegration.user_id == current_user.id,
SlackIntegration.organization_id.is_(None)
).first()
if personal_slack:
db.delete(personal_slack)
logger.info(f"Deleted personal Slack integration for user {current_user.email} joining org {invitation.organization_id}")
Copy link
Contributor

Choose a reason for hiding this comment

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

Same issue as above - SlackIntegration.organization_id does not exist and will cause runtime error

Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/app/api/endpoints/invitations.py
Line: 395:402

Comment:
Same issue as above - `SlackIntegration.organization_id` does not exist and will cause runtime error

How can I resolve this? If you propose a fix, please make it concise.

try:
# Delete user's personal Slack integration if they have one
# (organizations should have a single shared Slack workspace)
from app.models.slack import SlackIntegration
Copy link
Contributor

Choose a reason for hiding this comment

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

logger is used but not imported. Add at the top of the file:

Suggested change
from app.models.slack import SlackIntegration
import logging
logger = logging.getLogger(__name__)
Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/app/api/endpoints/invitations.py
Line: 302:302

Comment:
`logger` is used but not imported. Add at the top of the file:

```suggestion
import logging

logger = logging.getLogger(__name__)
```

How can I resolve this? If you propose a fix, please make it concise.

- Add missing logger import in invitations.py
- Update error messages to be more accurate (organization admins)
- Add comments clarifying atomic transaction behavior
- Fix: Changed 'Only admins can disconnect' to 'Only admins can manage'
- Fix: Changed 'Only admins can connect' to 'Only organization admins can connect'
@claude
Copy link

claude bot commented Feb 6, 2026

Claude finished @spencerhcheng's task —— View job


PR Code Review

Tasks:

  • Read and analyze backend changes in invitations.py
  • Read and analyze frontend changes in UnifiedSlackCard.tsx
  • Report findings focusing on bugs, security, and code quality issues only

Critical Issues Found

Backend (backend/app/api/endpoints/invitations.py)

Runtime Error - Lines 310 & 405:

SlackIntegration.organization_id.is_(None)
  • Issue: SlackIntegration model does not have an organization_id column. This will cause AttributeError at runtime.
  • Evidence: The model at backend/app/models/slack_integration.py:10-21 only has: id, user_id, slack_token, slack_user_id, workspace_id, webhook_url, token_source, created_at, updated_at.
  • Impact: Both invitation acceptance endpoints will fail when users try to join organizations.

Frontend (frontend/src/app/integrations/components/UnifiedSlackCard.tsx)

Permission Logic Error - Line 297:

toast.error('Only admins can manage Slack integration')
  • Issue: Error message is incorrect. The logic at lines 293-295 allows users without organization_id to disconnect, but the error message says only admins can do this action.
  • Impact: Users will receive misleading error messages about their permissions.

Potential XSS Risk - Lines 152-155:

const workspaceName = statusData.organization_workspace_mappings?.[0]?.workspace_name ||
                     statusData.user_workspace_mappings?.[0]?.workspace_name ||
                     'Unknown workspace'
toast.success(`✅ Workspace is properly registered! /oncall-health command should work.\n\nRegistered workspace: ${workspaceName}`)
  • Issue: workspaceName from API response is directly interpolated into toast message without sanitization.
  • Impact: Malicious workspace names could potentially execute script in toast notifications.

Required Fixes

  1. Backend: Add organization_id column to slack_integrations table and update the model, or modify the query logic to use a different approach for identifying personal vs organizational integrations.

  2. Frontend: Update error messages to accurately reflect permission logic.

  3. Frontend: Sanitize workspace names before displaying in toast messages.


- Add organization_id column to slack_integrations table (nullable)
- Create database migration script
- Add foreign key constraint to organizations table
- NULL indicates personal integration, set value indicates org-owned
- Fixes AttributeError that would occur when querying organization_id
@claude
Copy link

claude bot commented Feb 6, 2026

Claude finished @spencerhcheng's task —— View job


PR Code Review

Tasks:

  • Read and analyze all changed files
  • Check for bugs, security issues, and code quality problems
  • Report findings

Issues Found

Backend (backend/app/api/endpoints/invitations.py)

Import Path Mismatch - Lines 307 & 402:

from app.models.slack import SlackIntegration
  • Issue: Import path inconsistent with existing pattern. Other models in the file use relative imports from ...models
  • Expected: from ...models.slack_integration import SlackIntegration (based on actual file path backend/app/models/slack_integration.py)

Incorrect Model Reference - Lines 310 & 405:

SlackIntegration.organization_id.is_(None)
  • Issue: Model imports from wrong module name. The actual model file is slack_integration.py, not slack.py
  • Impact: Will cause ModuleNotFoundError at runtime

Frontend (frontend/src/app/integrations/components/UnifiedSlackCard.tsx)

Permission Error Message Mismatch - Line 297:

const canDisconnect = userInfo?.role === 'admin' ||
                     userInfo?.role === 'super_admin' ||
                     !userInfo?.organization_id

if (!canDisconnect) {
  toast.error('Only admins can manage Slack integration')
  • Issue: Error message states "Only admins" but logic allows users without organizations to disconnect
  • Fix: Update message to reflect actual permission logic

Potential XSS Risk - Lines 152-155:

const workspaceName = statusData.organization_workspace_mappings?.[0]?.workspace_name ||
                     statusData.user_workspace_mappings?.[0]?.workspace_name ||
                     'Unknown workspace'
toast.success(`✅ Workspace is properly registered! /oncall-health command should work.\n\nRegistered workspace: ${workspaceName}`)
  • Issue: Workspace name from API response directly interpolated into toast message without sanitization
  • Risk: Malicious workspace names could execute scripts in toast notifications

Database Schema Issue

Missing Migration Application:

  • The migration file 2026_02_06_add_organization_id_to_slack_integrations.sql exists but may not have been applied
  • Code references organization_id column that may not exist in database yet
  • Verify migration has been applied before deploying this code

- Add organization_id attribution to SlackIntegration on OAuth callback
- NULL for personal integrations (users without org)
- Set to user's organization_id for org-owned integrations
- Ensures proper tracking of integration ownership
@claude
Copy link

claude bot commented Feb 6, 2026

Claude finished @spencerhcheng's task —— View job


PR Code Review

Tasks:

  • Read repository guidelines (CLAUDE.md) - No CLAUDE.md found
  • Analyze backend changes in invitations.py
  • Analyze frontend changes in UnifiedSlackCard.tsx
  • Check for bugs, security issues, and code quality problems
  • Report findings

Issues Found

Backend (backend/app/api/endpoints/invitations.py)

Import Path Issue - Lines 307 & 402:

from app.models.slack import SlackIntegration
  • Issue: Import path incorrect. Should be from app.models.slack_integration import SlackIntegration based on actual file path at backend/app/models/slack_integration.py
  • Impact: Will cause ModuleNotFoundError at runtime

Frontend (frontend/src/app/integrations/components/UnifiedSlackCard.tsx)

Permission Error Message Mismatch - Line 297:

const canDisconnect = userInfo?.role === 'admin' ||
                     userInfo?.role === 'super_admin' ||
                     !userInfo?.organization_id

if (!canDisconnect) {
  toast.error('Only admins can manage Slack integration')
  • Issue: Error message states "Only admins" but logic allows users without organizations to disconnect
  • Fix: Update message to reflect actual permission logic

Potential XSS Risk - Lines 152-155:

const workspaceName = statusData.organization_workspace_mappings?.[0]?.workspace_name ||
                     statusData.user_workspace_mappings?.[0]?.workspace_name ||
                     'Unknown workspace'
toast.success(`✅ Workspace is properly registered! /oncall-health command should work.\n\nRegistered workspace: ${workspaceName}`)
  • Issue: workspaceName from API response directly interpolated into toast message without sanitization
  • Risk: Malicious workspace names could execute scripts in toast notifications

Database Schema Verification Required

The migration file 2026_02_06_add_organization_id_to_slack_integrations.sql correctly adds the organization_id column to the slack_integrations table, and the SlackIntegration model has been updated accordingly. The code should work correctly once the migration is applied.

- Fix import path: Use relative import from ...models.slack_integration
- Update error message to 'Only organization admins can manage'
- Sanitize workspace name to prevent XSS in toast notifications
- Strip HTML/script tags from workspace name before display
@claude
Copy link

claude bot commented Feb 7, 2026

Claude finished @spencerhcheng's task —— View job


PR Code Review

Tasks:

  • Read and analyze all changed files
  • Check for bugs, security issues, and code quality problems
  • Report findings

Issues Found

Frontend (frontend/src/app/integrations/components/UnifiedSlackCard.tsx)

Error Message Mismatch - Line 299:

const canDisconnect = userInfo?.role === 'admin' ||
                     userInfo?.role === 'super_admin' ||
                     !userInfo?.organization_id

if (!canDisconnect) {
  toast.error('Only organization admins can manage Slack integration')
  • Issue: Error message states "Only organization admins" but logic allows users without organizations to disconnect
  • Fix: Update message to reflect actual permission logic

XSS Prevention Applied - Lines 155-156:

  • Good: XSS mitigation was added with workspaceName.replace(/<[^>]*>/g, '')
  • Note: This prevents HTML injection in toast messages

View job

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