diff --git a/packages/core/package.nls.json b/packages/core/package.nls.json index 0a25550ec22..57fdaf87a4f 100644 --- a/packages/core/package.nls.json +++ b/packages/core/package.nls.json @@ -137,6 +137,7 @@ "AWS.command.codecatalyst.login": "Connect to CodeCatalyst", "AWS.command.codecatalyst.logout": "Sign out of CodeCatalyst", "AWS.command.codecatalyst.signout": "Sign Out", + "AWS.command.codecatalyst.showUserInfo": "Show User Information", "AWS.command.manageSubscription": "Manage Q Developer Pro Subscription", "AWS.command.amazonq.explainCode": "Explain", "AWS.command.amazonq.refactorCode": "Refactor", diff --git a/packages/core/src/codecatalyst/commands.ts b/packages/core/src/codecatalyst/commands.ts index 509d9b40d01..d8eae74a7d4 100644 --- a/packages/core/src/codecatalyst/commands.ts +++ b/packages/core/src/codecatalyst/commands.ts @@ -32,6 +32,41 @@ export async function listCommands(): Promise { await vscode.commands.executeCommand('workbench.action.quickOpen', '> CodeCatalyst') } +/** "Show User Info" command - displays current CodeCatalyst user details. */ +export async function showUserInfo(client: CodeCatalystClient): Promise { + try { + const userDetails = await client.verifySession() + + const userInfoMessage = [ + `**User Information**`, + ``, + `**User ID:** ${userDetails.userId}`, + `**Username:** ${userDetails.userName}`, + `**Display Name:** ${userDetails.displayName}`, + `**Email:** ${userDetails.primaryEmail}`, + ].join('\n') + + await vscode.window.showInformationMessage( + localize('aws.codecatalyst.userInfo.title', 'CodeCatalyst User Information'), + { + modal: true, + detail: userInfoMessage + } + ) + } catch (error) { + if (error instanceof ToolkitError) { + await vscode.window.showErrorMessage( + localize('aws.codecatalyst.userInfo.error', 'Failed to get user information: {0}', error.message) + ) + } else { + await vscode.window.showErrorMessage( + localize('aws.codecatalyst.userInfo.errorGeneric', 'Failed to get user information. Please ensure you are authenticated with CodeCatalyst.') + ) + } + throw error + } +} + /** "Clone CodeCatalyst Repository" command. */ export async function cloneCodeCatalystRepo(client: CodeCatalystClient, url?: vscode.Uri): Promise { let resource: { name: string; project: string; org: string } @@ -239,6 +274,10 @@ export class CodeCatalystCommands { return listCommands() } + public showUserInfo(_?: VsCodeCommandArg) { + return this.withClient(showUserInfo) + } + public cloneRepo(_?: VsCodeCommandArg, ...args: WithClient) { return this.withClient(cloneCodeCatalystRepo, ...args) } @@ -344,6 +383,7 @@ export class CodeCatalystCommands { public static readonly declared = { openResource: Commands.from(this).declareOpenResource('aws.codecatalyst.openResource'), listCommands: Commands.from(this).declareListCommands('aws.codecatalyst.listCommands'), + showUserInfo: Commands.declare('aws.codecatalyst.showUserInfo', (commands: CodeCatalystCommands) => () => commands.showUserInfo()), openSpace: Commands.from(this).declareOpenSpace('aws.codecatalyst.openOrg'), openProject: Commands.from(this).declareOpenProject('aws.codecatalyst.openProject'), openRepository: Commands.from(this).declareOpenRepository('aws.codecatalyst.openRepo'), diff --git a/packages/core/src/test/codecatalyst/commands.test.ts b/packages/core/src/test/codecatalyst/commands.test.ts new file mode 100644 index 00000000000..0547132b396 --- /dev/null +++ b/packages/core/src/test/codecatalyst/commands.test.ts @@ -0,0 +1,85 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import sinon from 'sinon' +import * as vscode from 'vscode' +import { showUserInfo } from '../../codecatalyst/commands' +import { CodeCatalystClient } from '../../shared/clients/codecatalystClient' + +describe('CodeCatalyst Commands', function () { + let sandbox: sinon.SinonSandbox + let mockClient: sinon.SinonStubbedInstance + let showInformationMessageStub: sinon.SinonStub + let showErrorMessageStub: sinon.SinonStub + + beforeEach(function () { + sandbox = sinon.createSandbox() + mockClient = sandbox.createStubInstance(Object as any) + showInformationMessageStub = sandbox.stub(vscode.window, 'showInformationMessage') + showErrorMessageStub = sandbox.stub(vscode.window, 'showErrorMessage') + }) + + afterEach(function () { + sandbox.restore() + }) + + describe('showUserInfo', function () { + it('displays user information when client returns valid data', async function () { + const mockUserDetails = { + userId: 'user-123', + userName: 'testuser', + displayName: 'Test User', + primaryEmail: 'test@example.com' + } + + mockClient.verifySession.resolves(mockUserDetails) + + await showUserInfo(mockClient as any) + + assert(showInformationMessageStub.calledOnce) + const [message, options] = showInformationMessageStub.firstCall.args + assert.strictEqual(message, 'CodeCatalyst User Information') + assert(options.modal) + assert(options.detail.includes('Test User')) + assert(options.detail.includes('test@example.com')) + }) + it('displays error message when client fails with ToolkitError', async function () { + const toolkitError = new ToolkitError('Authentication failed', { code: 'NoConnectionBadState' }) + mockClient.verifySession.rejects(toolkitError) + + try { + await showUserInfo(mockClient as any) + assert.fail('Expected error to be thrown') + } catch (error) { + assert(showErrorMessageStub.calledOnce) + const errorMessage = showErrorMessageStub.firstCall.args[0] + assert(errorMessage.includes('Authentication failed')) + } + }) + + it('displays generic error message when client fails with generic error', async function () { + mockClient.verifySession.rejects(new Error('Network error')) + + try { + await showUserInfo(mockClient as any) + assert.fail('Expected error to be thrown') + } catch (error) { + assert(showErrorMessageStub.calledOnce) + const errorMessage = showErrorMessageStub.firstCall.args[0] + assert(errorMessage.includes('Please ensure you are authenticated')) + } + }) + mockClient.verifySession.rejects(new Error('Authentication failed')) + + try { + await showUserInfo(mockClient as any) + assert.fail('Expected error to be thrown') + } catch (error) { + assert(showErrorMessageStub.calledOnce) + } + }) + }) +}) \ No newline at end of file diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json index 34fb02a8bd6..451d172fdd4 100644 --- a/packages/toolkit/package.json +++ b/packages/toolkit/package.json @@ -1424,6 +1424,13 @@ "when": "view == aws.codecatalyst && !isCloud9 && aws.codecatalyst.connected", "group": "1_codeCatalyst@1" }, + { + "command": "aws.codecatalyst.showUserInfo", + "when": "view == aws.codecatalyst && !isCloud9 && aws.codecatalyst.connected", + "group": "1_codeCatalyst@3" + }, + "group": "1_codeCatalyst@2" + }, { "command": "aws.codecatalyst.manageConnections", "when": "view == aws.codecatalyst && !isCloud9 && !aws.codecatalyst.connected", @@ -2540,6 +2547,12 @@ "icon": "$(debug-disconnect)", "enablement": "isCloud9 || !aws.isWebExtHost" }, + { + "command": "aws.codecatalyst.showUserInfo", + "title": "%AWS.command.codecatalyst.showUserInfo%", + "category": "AWS", + "enablement": "isCloud9 || !aws.isWebExtHost" + }, { "command": "aws.toolkit.auth.addConnection", "title": "%AWS.command.auth.addConnection%",