-
Notifications
You must be signed in to change notification settings - Fork 36.7k
Adding coreview for slack workspace extension #281508
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces a new VS Code extension for Slack workspace integration that enables code review workflows directly within VS Code. The extension provides a tree view to display Slack messages from a #codereview channel, with special handling for GitHub pull request links.
Key changes:
- Complete extension implementation with authentication, message fetching, and PR integration
- OAuth-based Slack authentication with session management
- GitHub API integration for fetching PR metadata
- Tree view UI for displaying code review messages
Reviewed changes
Copilot reviewed 14 out of 16 changed files in this pull request and generated 28 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.json | TypeScript configuration for the extension |
| src/test/extension.test.ts | Placeholder test suite (needs comprehensive tests) |
| src/slackTreeDataProvider.ts | Tree view data provider for Slack messages UI |
| src/slackService.ts | Core service for Slack API communication and GitHub integration |
| src/slackAuthenticationProvider.ts | OAuth authentication provider for Slack |
| src/extension.ts | Extension entry point with command and view registration |
| package.json | Extension manifest with commands, views, and dependencies |
| oauth-redirect/index.html | OAuth callback page for handling Slack authentication |
| oauth-redirect/.gitignore | Ignore file for deployment artifacts |
| eslint.config.mjs | ESLint configuration |
| esbuild.js | Build configuration with environment variable injection |
| .vscode/launch.json | VS Code debugging configuration |
| .gitignore | Added .env files to gitignore |
| async fetchMessages(): Promise<void> { | ||
| this.isLoading = true; | ||
| this.errorMessage = undefined; | ||
| this.refresh(); | ||
|
|
||
| try { | ||
| this.messages = await this.slackService.getMessages(); | ||
| } catch (error) { | ||
| this.errorMessage = error instanceof Error ? error.message : 'Unknown error'; | ||
| this.messages = []; | ||
| } finally { | ||
| this.isLoading = false; | ||
| this.refresh(); | ||
| this.onMessageCountChanged?.(this.messages.length); | ||
| } |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential race condition: if fetchMessages() is called multiple times concurrently (e.g., from auto-refresh and manual refresh), the isLoading flag could be set incorrectly. The second call would set isLoading = true again, but when either call completes, it sets isLoading = false, potentially allowing a third call to start before all previous calls have completed.
Consider using a promise-based approach to prevent concurrent fetches, or check if a fetch is already in progress before starting a new one.
| async fetchMessages(): Promise<void> { | |
| this.isLoading = true; | |
| this.errorMessage = undefined; | |
| this.refresh(); | |
| try { | |
| this.messages = await this.slackService.getMessages(); | |
| } catch (error) { | |
| this.errorMessage = error instanceof Error ? error.message : 'Unknown error'; | |
| this.messages = []; | |
| } finally { | |
| this.isLoading = false; | |
| this.refresh(); | |
| this.onMessageCountChanged?.(this.messages.length); | |
| } | |
| private fetchMessagesPromise: Promise<void> | null = null; | |
| async fetchMessages(): Promise<void> { | |
| // Prevent concurrent fetches | |
| if (this.fetchMessagesPromise) { | |
| return this.fetchMessagesPromise; | |
| } | |
| this.isLoading = true; | |
| this.errorMessage = undefined; | |
| this.refresh(); | |
| this.fetchMessagesPromise = (async () => { | |
| try { | |
| this.messages = await this.slackService.getMessages(); | |
| } catch (error) { | |
| this.errorMessage = error instanceof Error ? error.message : 'Unknown error'; | |
| this.messages = []; | |
| } finally { | |
| this.isLoading = false; | |
| this.refresh(); | |
| this.onMessageCountChanged?.(this.messages.length); | |
| this.fetchMessagesPromise = null; | |
| } | |
| })(); | |
| return this.fetchMessagesPromise; |
| suite('Extension Test Suite', () => { | ||
| vscode.window.showInformationMessage('Start all tests.'); | ||
|
|
||
| test('Sample test', () => { | ||
| assert.strictEqual(-1, [1, 2, 3].indexOf(5)); | ||
| assert.strictEqual(-1, [1, 2, 3].indexOf(0)); | ||
| }); |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test suite only contains a basic sample test that doesn't test any actual functionality of the extension. Consider adding tests for the key functionality including:
- SlackService authentication flows
- SlackTreeDataProvider message fetching and display
- Command handlers (sign in/out, open PR, refresh)
- PR URL extraction and GitHub API integration
- OAuth flow and state management
|
|
||
| return session; | ||
| } catch (error) { | ||
| console.error('Failed to get Slack session:', error); |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Console.error statements should not be used in production code. Use a proper logging framework or VS Code output channels instead.
|
|
||
| export interface SlackMessage { | ||
| id: string; | ||
| author: string; | ||
| text: string; | ||
| timestamp: string; | ||
| prUrl?: string; | ||
| prTitle?: string; | ||
| prAuthor?: string; | ||
| prOwner?: string; | ||
| prRepo?: string; | ||
| prNumber?: number; | ||
| prAdditions?: number; | ||
| prDeletions?: number; |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing JSDoc documentation for the public interface SlackMessage and its properties. Since this interface is exported and used across multiple files, it should have documentation explaining the purpose of each field.
Example:
/**
* Represents a message from the Slack code review channel.
*/
export interface SlackMessage {
/** Unique identifier for the message (Slack timestamp) */
id: string;
/** Display name of the message author */
author: string;
// ... etc
}| export interface SlackMessage { | |
| id: string; | |
| author: string; | |
| text: string; | |
| timestamp: string; | |
| prUrl?: string; | |
| prTitle?: string; | |
| prAuthor?: string; | |
| prOwner?: string; | |
| prRepo?: string; | |
| prNumber?: number; | |
| prAdditions?: number; | |
| prDeletions?: number; | |
| /** | |
| * Represents a message from the Slack code review channel. | |
| */ | |
| export interface SlackMessage { | |
| /** Unique identifier for the message (Slack timestamp) */ | |
| id: string; | |
| /** Display name of the message author */ | |
| author: string; | |
| /** The text content of the message */ | |
| text: string; | |
| /** The timestamp of the message (ISO string or Slack format) */ | |
| timestamp: string; | |
| /** The URL of the related pull request, if any */ | |
| prUrl?: string; | |
| /** The title of the related pull request, if any */ | |
| prTitle?: string; | |
| /** The author of the pull request (GitHub handle), if any */ | |
| prAuthor?: string; | |
| /** The owner of the repository for the pull request, if any */ | |
| prOwner?: string; | |
| /** The name of the repository for the pull request, if any */ | |
| prRepo?: string; | |
| /** The pull request number, if any */ | |
| prNumber?: number; | |
| /** Number of additions in the pull request, if any */ | |
| prAdditions?: number; | |
| /** Number of deletions in the pull request, if any */ | |
| prDeletions?: number; | |
| /** Number of changed files in the pull request, if any */ |
| export class SlackAuthenticationProvider implements vscode.AuthenticationProvider, vscode.Disposable { | ||
| private _sessionChangeEmitter = new vscode.EventEmitter<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>(); | ||
| private _disposable: vscode.Disposable; | ||
| private _sessions: vscode.AuthenticationSession[] = []; | ||
| private _pendingAuth: PendingAuth | undefined; | ||
| private _sessionsLoaded: boolean = false; | ||
|
|
||
| constructor(private readonly context: vscode.ExtensionContext) { |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing JSDoc documentation for the exported SlackAuthenticationProvider class. This class implements the VS Code authentication provider interface and should document how it handles OAuth flow, session management, and URI callbacks.
| try { | ||
| await vscode.env.openExternal(vscode.Uri.parse(item.message.prUrl)); | ||
| } catch (error) { | ||
| console.error('Failed to open PR in browser:', error); |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Console.error statements should not be used in production code. Use a proper logging framework or VS Code output channels instead.
| "commands": [ | ||
| { | ||
| "command": "vs-code-codereview.helloWorld", | ||
| "title": "Hello World" | ||
| }, | ||
| { | ||
| "command": "vs-code-codereview.refreshMessages", | ||
| "title": "Refresh Messages", | ||
| "icon": "$(refresh)" | ||
| }, | ||
| { | ||
| "command": "vs-code-codereview.signIn", | ||
| "title": "Sign in to Slack", | ||
| "icon": "$(sign-in)" | ||
| }, | ||
| { | ||
| "command": "vs-code-codereview.signOut", | ||
| "title": "Sign out from Slack", | ||
| "icon": "$(sign-out)" | ||
| }, | ||
| { | ||
| "command": "vs-code-codereview.openPrLocally", | ||
| "title": "Open PR Locally", | ||
| "icon": "$(link-external)" | ||
| }, | ||
| { | ||
| "command": "vs-code-codereview.openPrInBrowser", | ||
| "title": "Open PR in Browser", | ||
| "icon": "$(globe)" | ||
| } |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The command naming convention is inconsistent with the extension ID. The commands use 'vs-code-codereview' prefix while the package name is 'vs-code-codereview', but the authentication provider and internal references use different variations. For consistency, consider standardizing on a single naming scheme throughout (e.g., all commands should use the same prefix format as defined in the package.json name).
| } catch { | ||
| // Fall back to username if user info fetch fails |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error handling swallows the actual error without logging it. At minimum, the error should be logged to help with debugging when fetching additional user details fails.
| } catch { | |
| // Fall back to username if user info fetch fails | |
| } catch (err) { | |
| // Fall back to username if user info fetch fails | |
| console.error('Failed to fetch additional Slack user details:', err); |
| if (sessionsJson) { | ||
| try { | ||
| this._sessions = JSON.parse(sessionsJson); | ||
| } catch { |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error handling swallows the actual error without logging it. At minimum, the error should be logged to help with debugging when session loading fails.
| } catch { | |
| } catch (err) { | |
| console.error('Failed to parse Slack sessions from secrets:', err); |
| // Auto-refresh messages every 60 seconds | ||
| const REFRESH_INTERVAL_MS = 60 * 1000; // 1 minute | ||
| const autoRefreshInterval = setInterval(async () => { | ||
| const isAuthenticated = await slackService.isAuthenticated(); | ||
| if (isAuthenticated) { | ||
| await slackTreeDataProvider.fetchMessages(); | ||
| } | ||
| }, REFRESH_INTERVAL_MS); | ||
|
|
||
| // Clean up the interval when the extension is deactivated | ||
| context.subscriptions.push({ | ||
| dispose: () => clearInterval(autoRefreshInterval) |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The hardcoded 60-second refresh interval may be too aggressive for production use. Consider making this configurable via VS Code settings to allow users to adjust the polling frequency based on their needs. Frequent API calls could hit rate limits on both Slack and GitHub APIs.
| // Auto-refresh messages every 60 seconds | |
| const REFRESH_INTERVAL_MS = 60 * 1000; // 1 minute | |
| const autoRefreshInterval = setInterval(async () => { | |
| const isAuthenticated = await slackService.isAuthenticated(); | |
| if (isAuthenticated) { | |
| await slackTreeDataProvider.fetchMessages(); | |
| } | |
| }, REFRESH_INTERVAL_MS); | |
| // Clean up the interval when the extension is deactivated | |
| context.subscriptions.push({ | |
| dispose: () => clearInterval(autoRefreshInterval) | |
| // Auto-refresh messages at a configurable interval | |
| let refreshIntervalSeconds = vscode.workspace.getConfiguration('codereview-slack').get<number>('refreshIntervalSeconds', 60); | |
| let autoRefreshInterval: NodeJS.Timeout | undefined; | |
| const startAutoRefresh = () => { | |
| if (autoRefreshInterval) { | |
| clearInterval(autoRefreshInterval); | |
| } | |
| autoRefreshInterval = setInterval(async () => { | |
| const isAuthenticated = await slackService.isAuthenticated(); | |
| if (isAuthenticated) { | |
| await slackTreeDataProvider.fetchMessages(); | |
| } | |
| }, refreshIntervalSeconds * 1000); | |
| }; | |
| startAutoRefresh(); | |
| // Listen for configuration changes to update the interval dynamically | |
| const configChangeDisposable = vscode.workspace.onDidChangeConfiguration(e => { | |
| if (e.affectsConfiguration('codereview-slack.refreshIntervalSeconds')) { | |
| refreshIntervalSeconds = vscode.workspace.getConfiguration('codereview-slack').get<number>('refreshIntervalSeconds', 60); | |
| startAutoRefresh(); | |
| } | |
| }); | |
| context.subscriptions.push(configChangeDisposable); | |
| // Clean up the interval when the extension is deactivated | |
| context.subscriptions.push({ | |
| dispose: () => { | |
| if (autoRefreshInterval) { | |
| clearInterval(autoRefreshInterval); | |
| } | |
| } |
…build scripts, and add esbuild configuration - Removed the "Hello World" command from package.json. - Updated build scripts in package.json to use Gulp for compilation and watching. - Added esbuild.mjs for building the extension with environment variable support. - Enhanced command disposal in commands.ts and view.ts to call super.dispose(). - Updated mcp.json to include configuration for vscode-playwright-mcp server.
…g in Slack service
…dling and improving token exchange process
- Updated command identifiers for opening PRs to improve clarity. - Removed the authentication provider implementation and moved it to a new file for better organization. - Simplified command registration by reducing dependencies on the Slack service. - Enhanced error handling and user feedback during authentication. - Implemented auto-refresh for message fetching in the Slack tree data provider. - Improved session management and caching for user and pull request data. - Consolidated authentication variables into a dedicated module for easier configuration.
…s and update usage in SlackTreeDataProvider
No description provided.