From a16fd11baab55b4cafc49713dec3333f161605e7 Mon Sep 17 00:00:00 2001 From: Bodie Weedop Date: Thu, 22 Aug 2024 08:47:06 -0700 Subject: [PATCH 01/14] feat (Amazon Q): Add basic UX and logic for generating unit tests --- packages/amazonq/package.json | 20 ++++++++++--- packages/core/package.nls.json | 1 + .../commands/registerCommands.ts | 9 ++++++ .../controllers/chat/controller.ts | 28 ++++++++++++++++++- .../controllers/chat/messenger/messenger.ts | 14 +++++++++- .../chat/prompts/promptsGenerator.ts | 1 + .../chat/userIntent/userIntentRecognizer.ts | 1 + .../core/src/shared/settings-amazonq.gen.ts | 3 +- 8 files changed, 69 insertions(+), 8 deletions(-) diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index 779fadef790..e44c24818e1 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -106,10 +106,6 @@ "amazonQWelcomePage": { "type": "boolean", "default": false - }, - "amazonQSessionConfigurationMessage": { - "type": "boolean", - "default": false } }, "additionalProperties": false @@ -348,6 +344,10 @@ { "command": "aws.amazonq.sendToPrompt", "group": "cw_chat@5" + }, + { + "command": "aws.amazonq.testCode", + "group": "cw_chat@6" } ], "editor/context": [ @@ -419,6 +419,12 @@ "category": "%AWS.amazonq.title%", "enablement": "aws.codewhisperer.connected" }, + { + "command": "aws.amazonq.testCode", + "title": "%AWS.command.amazonq.testCode%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, { "command": "aws.amazonq.reconnect", "title": "%AWS.command.codewhisperer.reconnect%", @@ -595,6 +601,12 @@ "mac": "cmd+alt+q", "linux": "meta+alt+q" }, + { + "command": "aws.amazonq.testCode", + "key": "win+alt+t", + "mac": "cmd+alt+t", + "linux": "meta+alt+t" + }, { "command": "aws.amazonq.invokeInlineCompletion", "key": "alt+c", diff --git a/packages/core/package.nls.json b/packages/core/package.nls.json index 988bef0dfb8..c038a8df9cd 100644 --- a/packages/core/package.nls.json +++ b/packages/core/package.nls.json @@ -111,6 +111,7 @@ "AWS.command.amazonq.fixCode": "Fix", "AWS.command.amazonq.optimizeCode": "Optimize", "AWS.command.amazonq.sendToPrompt": "Send to prompt", + "AWS.command.amazonq.testCode": "Test", "AWS.command.amazonq.security.scan": "Run Project Scan", "AWS.command.deploySamApplication": "Deploy SAM Application", "AWS.command.aboutToolkit": "About", diff --git a/packages/core/src/codewhispererChat/commands/registerCommands.ts b/packages/core/src/codewhispererChat/commands/registerCommands.ts index 21e65bc6c83..8ec0af9b02a 100644 --- a/packages/core/src/codewhispererChat/commands/registerCommands.ts +++ b/packages/core/src/codewhispererChat/commands/registerCommands.ts @@ -94,6 +94,14 @@ export function registerCommands(controllerPublishers: ChatControllerMessagePubl }) }) }) + Commands.register('aws.amazonq.testCode', async (data) => { + return focusAmazonQPanel.execute(placeholder, 'amazonq.testCode').then(() => { + controllerPublishers.processContextMenuCommand.publish({ + type: 'aws.amazonq.testCode', + triggerType: getCommandTriggerType(data), + }) + }) + }) } export type EditorContextBaseCommandType = @@ -102,6 +110,7 @@ export type EditorContextBaseCommandType = | 'aws.amazonq.fixCode' | 'aws.amazonq.optimizeCode' | 'aws.amazonq.sendToPrompt' + | 'aws.amazonq.testCode' export type CodeScanIssueCommandType = 'aws.amazonq.explainIssue' diff --git a/packages/core/src/codewhispererChat/controllers/chat/controller.ts b/packages/core/src/codewhispererChat/controllers/chat/controller.ts index 9baa5bef047..63072f536c7 100644 --- a/packages/core/src/codewhispererChat/controllers/chat/controller.ts +++ b/packages/core/src/codewhispererChat/controllers/chat/controller.ts @@ -2,7 +2,7 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ -import { Event as VSCodeEvent, Uri } from 'vscode' +import { Event as VSCodeEvent, Uri, workspace as VSCodeWorkspace, window as VSCodeWindow } from 'vscode' import { EditorContextExtractor } from '../../editor/context/extractor' import { ChatSessionStorage } from '../../storages/chatSession' import { Messenger, StaticTextResponseType } from './messenger/messenger' @@ -313,6 +313,14 @@ export class ChatController { this.sessionStorage.deleteSession(tabID) } + private async openInUntitledDocument(content: string, language?: string) { + const document = await VSCodeWorkspace.openTextDocument({ + language, + content, + }) + await VSCodeWindow.showTextDocument(document) + } + private async processContextMenuCommand(command: EditorContextCommand) { // Just open the chat panel in this case if (!this.editorContextExtractor.isCodeBlockSelected() && command.type === 'aws.amazonq.sendToPrompt') { @@ -362,6 +370,13 @@ export class ChatController { command, }) + if (command.type === 'aws.amazonq.testCode') { + void this.generateStaticTextResponse('generate-tests', triggerID) + // TODO: call RTS and get unit test generation + void this.openInUntitledDocument('some test', context?.activeFileContext?.fileLanguage) + return + } + return this.generateResponse( { message: prompt, @@ -460,6 +475,10 @@ export class ChatController { } } + private promptIsAskingToGenerateTests(prompt: PromptMessage): boolean { + return prompt.message?.includes('Generate test') ?? false + } + private async processPromptMessageAsNewThread(message: PromptMessage) { this.editorContextExtractor .extractContextForTrigger('ChatMessage') @@ -472,6 +491,13 @@ export class ChatController { type: 'chat_message', context, }) + + if (this.promptIsAskingToGenerateTests(message)) { + void this.generateStaticTextResponse('generate-tests', triggerID) + void this.openInUntitledDocument('some test', context?.activeFileContext?.fileLanguage) + return + } + return this.generateResponse( { message: message.message, diff --git a/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts b/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts index 0a9a42ec101..c201de5e1de 100644 --- a/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts +++ b/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts @@ -34,7 +34,7 @@ import { marked } from 'marked' import { JSDOM } from 'jsdom' import { LspController } from '../../../../amazonq/lsp/lspController' -export type StaticTextResponseType = 'quick-action-help' | 'onboarding-help' | 'transform' | 'help' +export type StaticTextResponseType = 'quick-action-help' | 'onboarding-help' | 'transform' | 'help' | 'generate-tests' export class Messenger { public constructor( @@ -339,6 +339,7 @@ export class Messenger { ['aws.amazonq.fixCode', 'Fix'], ['aws.amazonq.optimizeCode', 'Optimize'], ['aws.amazonq.sendToPrompt', 'Send to prompt'], + ['aws.amazonq.testCode', 'Generate tests'], ]) public sendStaticTextResponse(type: StaticTextResponseType, triggerID: string, tabID: string) { @@ -376,6 +377,9 @@ export class Messenger { \n\n- Do not enter any confidential, sensitive, or personal information. \n\n*For additional help, visit the [Amazon Q User Guide](${userGuideURL}).*` break + case 'generate-tests': + message = `I have generated tests for the selected code. I am sending the tests to a new tab.` + break case 'onboarding-help': message = `### What I can do: \n\n- Answer questions about AWS @@ -457,6 +461,14 @@ export class Messenger { trimmedCode, '\n```', ].join('') + } else if (command === 'aws.amazonq.testCode') { + message = [ + this.editorContextMenuCommandVerbs.get(command), + ' the following code:', + '\n```\n', + trimmedCode, + '\n```', + ].join('') } else { message = [ this.editorContextMenuCommandVerbs.get(command), diff --git a/packages/core/src/codewhispererChat/controllers/chat/prompts/promptsGenerator.ts b/packages/core/src/codewhispererChat/controllers/chat/prompts/promptsGenerator.ts index 6abecda2f8c..7ef6733a389 100644 --- a/packages/core/src/codewhispererChat/controllers/chat/prompts/promptsGenerator.ts +++ b/packages/core/src/codewhispererChat/controllers/chat/prompts/promptsGenerator.ts @@ -14,6 +14,7 @@ export class PromptsGenerator { ['aws.amazonq.fixCode', 'Fix'], ['aws.amazonq.optimizeCode', 'Optimize'], ['aws.amazonq.sendToPrompt', 'Send to prompt'], + ['aws.amazonq.testCode', 'Generate tests for'], ]) public generateForContextMenuCommand(command: EditorContextCommand): string { diff --git a/packages/core/src/codewhispererChat/controllers/chat/userIntent/userIntentRecognizer.ts b/packages/core/src/codewhispererChat/controllers/chat/userIntent/userIntentRecognizer.ts index e87a2ed93e6..3e24acaef0a 100644 --- a/packages/core/src/codewhispererChat/controllers/chat/userIntent/userIntentRecognizer.ts +++ b/packages/core/src/codewhispererChat/controllers/chat/userIntent/userIntentRecognizer.ts @@ -18,6 +18,7 @@ export class UserIntentRecognizer { return UserIntent.APPLY_COMMON_BEST_PRACTICES case 'aws.amazonq.optimizeCode': return UserIntent.IMPROVE_CODE + //TODO: Add user intent recognizer for generating tests default: return undefined } diff --git a/packages/core/src/shared/settings-amazonq.gen.ts b/packages/core/src/shared/settings-amazonq.gen.ts index 166ca9e3fa2..53a56150095 100644 --- a/packages/core/src/shared/settings-amazonq.gen.ts +++ b/packages/core/src/shared/settings-amazonq.gen.ts @@ -15,8 +15,7 @@ export const amazonqSettings = { "createCredentialsProfile": {}, "codeWhispererNewWelcomeMessage": {}, "codeWhispererConnectionExpired": {}, - "amazonQWelcomePage": {}, - "amazonQSessionConfigurationMessage": {} + "amazonQWelcomePage": {} }, "amazonQ.showInlineCodeSuggestionsWithCodeReferences": {}, "amazonQ.importRecommendationForInlineCodeSuggestions": {}, From 5cc1126bfdfe5530c891dd234fb02f4542591cf3 Mon Sep 17 00:00:00 2001 From: Bodie Weedop Date: Wed, 11 Sep 2024 13:11:49 -0700 Subject: [PATCH 02/14] Add GENERATE_UNIT_TESTS to model; Limit access to test command to internal users --- packages/amazonq/package.json | 20 ++++++--------- .../core/src/codewhisperer/util/authUtil.ts | 2 ++ .../controllers/chat/controller.ts | 25 ------------------- .../controllers/chat/messenger/messenger.ts | 13 +--------- .../chat/userIntent/userIntentRecognizer.ts | 9 ++++++- packages/core/src/shared/vscode/setContext.ts | 1 + .../GenerateAssistantResponseCommand.ts | 10 ++++---- .../commands/GenerateTaskAssistPlanCommand.ts | 10 ++++---- .../src/models/models_0.ts | 4 +++ 9 files changed, 33 insertions(+), 61 deletions(-) diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index c51a6e07562..cb036b78246 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -106,14 +106,6 @@ "amazonQWelcomePage": { "type": "boolean", "default": false - }, - "amazonQSessionConfigurationMessage": { - "type": "boolean", - "default": false - }, - "ssoCacheError": { - "type": "boolean", - "default": false } }, "additionalProperties": false @@ -350,11 +342,12 @@ "group": "cw_chat@4" }, { - "command": "aws.amazonq.sendToPrompt", - "group": "cw_chat@5" + "command": "aws.amazonq.testCode", + "group": "cw_chat@5", + "when": "aws.codewhisperer.connected && aws.isInternalUser" }, { - "command": "aws.amazonq.testCode", + "command": "aws.amazonq.sendToPrompt", "group": "cw_chat@6" } ], @@ -431,7 +424,7 @@ "command": "aws.amazonq.testCode", "title": "%AWS.command.amazonq.testCode%", "category": "%AWS.amazonq.title%", - "enablement": "aws.codewhisperer.connected" + "enablement": "aws.codewhisperer.connected && aws.isInternalUser" }, { "command": "aws.amazonq.reconnect", @@ -613,7 +606,8 @@ "command": "aws.amazonq.testCode", "key": "win+alt+t", "mac": "cmd+alt+t", - "linux": "meta+alt+t" + "linux": "meta+alt+t", + "when": "aws.codewhisperer.connected && aws.isInternalUser" }, { "command": "aws.amazonq.invokeInlineCompletion", diff --git a/packages/core/src/codewhisperer/util/authUtil.ts b/packages/core/src/codewhisperer/util/authUtil.ts index f603f8c68c1..af9c80e0206 100644 --- a/packages/core/src/codewhisperer/util/authUtil.ts +++ b/packages/core/src/codewhisperer/util/authUtil.ts @@ -135,6 +135,7 @@ export class AuthUtil { ]) await setContext('aws.codewhisperer.connected', this.isConnected()) + await setContext('aws.isInternalUser', this.startUrl === 'https://amzn.awsapps.com/start') // To check valid connection if (this.isValidEnterpriseSsoInUse() || (this.isBuilderIdInUse() && !this.isConnectionExpired())) { @@ -151,6 +152,7 @@ export class AuthUtil { } await setContext('aws.codewhisperer.connected', this.isConnected()) + await setContext('aws.isInternalUser', this.startUrl === 'https://amzn.awsapps.com/start') const doShowAmazonQLoginView = !this.isConnected() || this.isConnectionExpired() await setContext('aws.amazonq.showLoginView', doShowAmazonQLoginView) await setContext('aws.codewhisperer.connectionExpired', this.isConnectionExpired()) diff --git a/packages/core/src/codewhispererChat/controllers/chat/controller.ts b/packages/core/src/codewhispererChat/controllers/chat/controller.ts index 6c47bf13881..964ce4f46eb 100644 --- a/packages/core/src/codewhispererChat/controllers/chat/controller.ts +++ b/packages/core/src/codewhispererChat/controllers/chat/controller.ts @@ -313,14 +313,6 @@ export class ChatController { this.sessionStorage.deleteSession(tabID) } - private async openInUntitledDocument(content: string, language?: string) { - const document = await VSCodeWorkspace.openTextDocument({ - language, - content, - }) - await VSCodeWindow.showTextDocument(document) - } - private async processContextMenuCommand(command: EditorContextCommand) { // Just open the chat panel in this case if (!this.editorContextExtractor.isCodeBlockSelected() && command.type === 'aws.amazonq.sendToPrompt') { @@ -370,13 +362,6 @@ export class ChatController { command, }) - if (command.type === 'aws.amazonq.testCode') { - void this.generateStaticTextResponse('generate-tests', triggerID) - // TODO: call RTS and get unit test generation - void this.openInUntitledDocument('some test', context?.activeFileContext?.fileLanguage) - return - } - return this.generateResponse( { message: prompt, @@ -475,10 +460,6 @@ export class ChatController { } } - private promptIsAskingToGenerateTests(prompt: PromptMessage): boolean { - return prompt.message?.includes('Generate test') ?? false - } - private async processPromptMessageAsNewThread(message: PromptMessage) { this.editorContextExtractor .extractContextForTrigger('ChatMessage') @@ -492,12 +473,6 @@ export class ChatController { context, }) - if (this.promptIsAskingToGenerateTests(message)) { - void this.generateStaticTextResponse('generate-tests', triggerID) - void this.openInUntitledDocument('some test', context?.activeFileContext?.fileLanguage) - return - } - return this.generateResponse( { message: message.message, diff --git a/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts b/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts index c201de5e1de..472d948c2cd 100644 --- a/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts +++ b/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts @@ -339,7 +339,7 @@ export class Messenger { ['aws.amazonq.fixCode', 'Fix'], ['aws.amazonq.optimizeCode', 'Optimize'], ['aws.amazonq.sendToPrompt', 'Send to prompt'], - ['aws.amazonq.testCode', 'Generate tests'], + ['aws.amazonq.testCode', 'Generate unit tests for'], ]) public sendStaticTextResponse(type: StaticTextResponseType, triggerID: string, tabID: string) { @@ -377,9 +377,6 @@ export class Messenger { \n\n- Do not enter any confidential, sensitive, or personal information. \n\n*For additional help, visit the [Amazon Q User Guide](${userGuideURL}).*` break - case 'generate-tests': - message = `I have generated tests for the selected code. I am sending the tests to a new tab.` - break case 'onboarding-help': message = `### What I can do: \n\n- Answer questions about AWS @@ -461,14 +458,6 @@ export class Messenger { trimmedCode, '\n```', ].join('') - } else if (command === 'aws.amazonq.testCode') { - message = [ - this.editorContextMenuCommandVerbs.get(command), - ' the following code:', - '\n```\n', - trimmedCode, - '\n```', - ].join('') } else { message = [ this.editorContextMenuCommandVerbs.get(command), diff --git a/packages/core/src/codewhispererChat/controllers/chat/userIntent/userIntentRecognizer.ts b/packages/core/src/codewhispererChat/controllers/chat/userIntent/userIntentRecognizer.ts index 3e24acaef0a..5849e0a2ceb 100644 --- a/packages/core/src/codewhispererChat/controllers/chat/userIntent/userIntentRecognizer.ts +++ b/packages/core/src/codewhispererChat/controllers/chat/userIntent/userIntentRecognizer.ts @@ -6,6 +6,7 @@ import { UserIntent } from '@amzn/codewhisperer-streaming' import { EditorContextCommand } from '../../../commands/registerCommands' import { PromptMessage } from '../model' +import { AuthUtil } from '../../../../codewhisperer/util/authUtil' export class UserIntentRecognizer { public getFromContextMenuCommand(command: EditorContextCommand): UserIntent | undefined { @@ -18,7 +19,8 @@ export class UserIntentRecognizer { return UserIntent.APPLY_COMMON_BEST_PRACTICES case 'aws.amazonq.optimizeCode': return UserIntent.IMPROVE_CODE - //TODO: Add user intent recognizer for generating tests + case 'aws.amazonq.testCode': + return UserIntent.GENERATE_UNIT_TESTS default: return undefined } @@ -37,6 +39,11 @@ export class UserIntentRecognizer { return UserIntent.APPLY_COMMON_BEST_PRACTICES } else if (prompt.message.startsWith('Optimize')) { return UserIntent.IMPROVE_CODE + } else if ( + prompt.message.startsWith('Generate unit tests') && + AuthUtil.instance.startUrl === 'https://amzn.awsapps.com/start' + ) { + return UserIntent.GENERATE_UNIT_TESTS } return undefined } diff --git a/packages/core/src/shared/vscode/setContext.ts b/packages/core/src/shared/vscode/setContext.ts index 9cf7c33c25f..5068bc59e13 100644 --- a/packages/core/src/shared/vscode/setContext.ts +++ b/packages/core/src/shared/vscode/setContext.ts @@ -14,6 +14,7 @@ type contextKey = | 'aws.isDevMode' | 'aws.isSageMaker' | 'aws.isWebExtHost' + | 'aws.isInternalUser' | 'aws.amazonq.showLoginView' | 'aws.codecatalyst.connected' | 'aws.codewhisperer.connected' diff --git a/src.gen/@amzn/codewhisperer-streaming/src/commands/GenerateAssistantResponseCommand.ts b/src.gen/@amzn/codewhisperer-streaming/src/commands/GenerateAssistantResponseCommand.ts index 769ecff549f..87e0f6fa51f 100644 --- a/src.gen/@amzn/codewhisperer-streaming/src/commands/GenerateAssistantResponseCommand.ts +++ b/src.gen/@amzn/codewhisperer-streaming/src/commands/GenerateAssistantResponseCommand.ts @@ -189,7 +189,7 @@ export interface GenerateAssistantResponseCommandOutput extends GenerateAssistan * hasConsentedToCrossRegionCalls: true || false, * }, * }, - * userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE", + * userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE" || "GENERATE_UNIT_TESTS", * }, * assistantResponseMessage: { // AssistantResponseMessage * messageId: "STRING_VALUE", @@ -214,7 +214,7 @@ export interface GenerateAssistantResponseCommandOutput extends GenerateAssistan * ], * followupPrompt: { // FollowupPrompt * content: "STRING_VALUE", // required - * userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE", + * userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE" || "GENERATE_UNIT_TESTS", * }, * }, * }, @@ -305,7 +305,7 @@ export interface GenerateAssistantResponseCommandOutput extends GenerateAssistan * hasConsentedToCrossRegionCalls: true || false, * }, * }, - * userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE", + * userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE" || "GENERATE_UNIT_TESTS", * }, * assistantResponseMessage: { * messageId: "STRING_VALUE", @@ -330,7 +330,7 @@ export interface GenerateAssistantResponseCommandOutput extends GenerateAssistan * ], * followupPrompt: { * content: "STRING_VALUE", // required - * userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE", + * userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE" || "GENERATE_UNIT_TESTS", * }, * }, * }, @@ -375,7 +375,7 @@ export interface GenerateAssistantResponseCommandOutput extends GenerateAssistan * // followupPromptEvent: { // FollowupPromptEvent * // followupPrompt: { // FollowupPrompt * // content: "STRING_VALUE", // required - * // userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE", + * // userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE" || "GENERATE_UNIT_TESTS", * // }, * // }, * // codeEvent: { // CodeEvent diff --git a/src.gen/@amzn/codewhisperer-streaming/src/commands/GenerateTaskAssistPlanCommand.ts b/src.gen/@amzn/codewhisperer-streaming/src/commands/GenerateTaskAssistPlanCommand.ts index fc884f5f339..22a591f22d8 100644 --- a/src.gen/@amzn/codewhisperer-streaming/src/commands/GenerateTaskAssistPlanCommand.ts +++ b/src.gen/@amzn/codewhisperer-streaming/src/commands/GenerateTaskAssistPlanCommand.ts @@ -189,7 +189,7 @@ export interface GenerateTaskAssistPlanCommandOutput extends GenerateTaskAssistP * hasConsentedToCrossRegionCalls: true || false, * }, * }, - * userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE", + * userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE" || "GENERATE_UNIT_TESTS", * }, * assistantResponseMessage: { // AssistantResponseMessage * messageId: "STRING_VALUE", @@ -214,7 +214,7 @@ export interface GenerateTaskAssistPlanCommandOutput extends GenerateTaskAssistP * ], * followupPrompt: { // FollowupPrompt * content: "STRING_VALUE", // required - * userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE", + * userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE" || "GENERATE_UNIT_TESTS", * }, * }, * }, @@ -305,7 +305,7 @@ export interface GenerateTaskAssistPlanCommandOutput extends GenerateTaskAssistP * hasConsentedToCrossRegionCalls: true || false, * }, * }, - * userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE", + * userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE" || "GENERATE_UNIT_TESTS", * }, * assistantResponseMessage: { * messageId: "STRING_VALUE", @@ -330,7 +330,7 @@ export interface GenerateTaskAssistPlanCommandOutput extends GenerateTaskAssistP * ], * followupPrompt: { * content: "STRING_VALUE", // required - * userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE", + * userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE" || "GENERATE_UNIT_TESTS", * }, * }, * }, @@ -378,7 +378,7 @@ export interface GenerateTaskAssistPlanCommandOutput extends GenerateTaskAssistP * // followupPromptEvent: { // FollowupPromptEvent * // followupPrompt: { // FollowupPrompt * // content: "STRING_VALUE", // required - * // userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE", + * // userIntent: "SUGGEST_ALTERNATE_IMPLEMENTATION" || "APPLY_COMMON_BEST_PRACTICES" || "IMPROVE_CODE" || "SHOW_EXAMPLES" || "CITE_SOURCES" || "EXPLAIN_LINE_BY_LINE" || "EXPLAIN_CODE_SELECTION" || "GENERATE_CLOUDFORMATION_TEMPLATE" || "GENERATE_UNIT_TESTS", * // }, * // }, * // codeEvent: { // CodeEvent diff --git a/src.gen/@amzn/codewhisperer-streaming/src/models/models_0.ts b/src.gen/@amzn/codewhisperer-streaming/src/models/models_0.ts index fa424d76496..7d2622c985e 100644 --- a/src.gen/@amzn/codewhisperer-streaming/src/models/models_0.ts +++ b/src.gen/@amzn/codewhisperer-streaming/src/models/models_0.ts @@ -245,6 +245,10 @@ export const UserIntent = { * Generate CloudFormation Template */ GENERATE_CLOUDFORMATION_TEMPLATE: "GENERATE_CLOUDFORMATION_TEMPLATE", + /** + * Generate Unit Tests + */ + GENERATE_UNIT_TESTS: "GENERATE_UNIT_TESTS", /** * Improve Code */ From f2ff8f71f2f0c68e1db55704e69d74741ad73c70 Mon Sep 17 00:00:00 2001 From: Bodie Weedop Date: Wed, 11 Sep 2024 13:14:03 -0700 Subject: [PATCH 03/14] Correct package.json --- packages/amazonq/package.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index cb036b78246..bd8fdb14446 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -106,6 +106,14 @@ "amazonQWelcomePage": { "type": "boolean", "default": false + }, + "amazonQSessionConfigurationMessage": { + "type": "boolean", + "default": false + }, + "ssoCacheError": { + "type": "boolean", + "default": false } }, "additionalProperties": false From 50fc1f89c42fc99328655c5da7ec2692492c1863 Mon Sep 17 00:00:00 2001 From: Bodie Weedop Date: Wed, 11 Sep 2024 13:56:17 -0700 Subject: [PATCH 04/14] Add Beta suffix to generate unit tests command --- packages/core/package.nls.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/package.nls.json b/packages/core/package.nls.json index c038a8df9cd..4c5f92e02ff 100644 --- a/packages/core/package.nls.json +++ b/packages/core/package.nls.json @@ -111,7 +111,7 @@ "AWS.command.amazonq.fixCode": "Fix", "AWS.command.amazonq.optimizeCode": "Optimize", "AWS.command.amazonq.sendToPrompt": "Send to prompt", - "AWS.command.amazonq.testCode": "Test", + "AWS.command.amazonq.testCode": "Test (Beta)", "AWS.command.amazonq.security.scan": "Run Project Scan", "AWS.command.deploySamApplication": "Deploy SAM Application", "AWS.command.aboutToolkit": "About", From 334dd2af04c16c65ae728282c1899b64fac22bda Mon Sep 17 00:00:00 2001 From: Bodie Weedop Date: Wed, 11 Sep 2024 14:40:27 -0700 Subject: [PATCH 05/14] Refactor to reduce code duplication --- packages/amazonq/package.json | 8 -------- packages/core/src/codewhisperer/util/authUtil.ts | 8 ++++++-- .../controllers/chat/messenger/messenger.ts | 2 +- .../controllers/chat/prompts/promptsGenerator.ts | 2 +- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index bd8fdb14446..cb036b78246 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -106,14 +106,6 @@ "amazonQWelcomePage": { "type": "boolean", "default": false - }, - "amazonQSessionConfigurationMessage": { - "type": "boolean", - "default": false - }, - "ssoCacheError": { - "type": "boolean", - "default": false } }, "additionalProperties": false diff --git a/packages/core/src/codewhisperer/util/authUtil.ts b/packages/core/src/codewhisperer/util/authUtil.ts index af9c80e0206..80bb9d7d812 100644 --- a/packages/core/src/codewhisperer/util/authUtil.ts +++ b/packages/core/src/codewhisperer/util/authUtil.ts @@ -135,7 +135,7 @@ export class AuthUtil { ]) await setContext('aws.codewhisperer.connected', this.isConnected()) - await setContext('aws.isInternalUser', this.startUrl === 'https://amzn.awsapps.com/start') + await setContext('aws.isInternalUser', this.isInternalAmazonUser()) // To check valid connection if (this.isValidEnterpriseSsoInUse() || (this.isBuilderIdInUse() && !this.isConnectionExpired())) { @@ -152,7 +152,7 @@ export class AuthUtil { } await setContext('aws.codewhisperer.connected', this.isConnected()) - await setContext('aws.isInternalUser', this.startUrl === 'https://amzn.awsapps.com/start') + await setContext('aws.isInternalUser', this.isInternalAmazonUser()) const doShowAmazonQLoginView = !this.isConnected() || this.isConnectionExpired() await setContext('aws.amazonq.showLoginView', doShowAmazonQLoginView) await setContext('aws.codewhisperer.connectionExpired', this.isConnectionExpired()) @@ -198,6 +198,10 @@ export class AuthUtil { return this.conn !== undefined && isBuilderIdConnection(this.conn) } + public isInternalAmazonUser(): boolean { + return this.isConnected() && this.startUrl === 'https://amzn.awsapps.com/start' + } + @withTelemetryContext({ name: 'connectToAwsBuilderId', class: authClassName }) public async connectToAwsBuilderId(): Promise { let conn = (await this.auth.listConnections()).find(isBuilderIdConnection) diff --git a/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts b/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts index 472d948c2cd..fe3d8242218 100644 --- a/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts +++ b/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts @@ -34,7 +34,7 @@ import { marked } from 'marked' import { JSDOM } from 'jsdom' import { LspController } from '../../../../amazonq/lsp/lspController' -export type StaticTextResponseType = 'quick-action-help' | 'onboarding-help' | 'transform' | 'help' | 'generate-tests' +export type StaticTextResponseType = 'quick-action-help' | 'onboarding-help' | 'transform' | 'help' export class Messenger { public constructor( diff --git a/packages/core/src/codewhispererChat/controllers/chat/prompts/promptsGenerator.ts b/packages/core/src/codewhispererChat/controllers/chat/prompts/promptsGenerator.ts index 7ef6733a389..06eeaa4ef8c 100644 --- a/packages/core/src/codewhispererChat/controllers/chat/prompts/promptsGenerator.ts +++ b/packages/core/src/codewhispererChat/controllers/chat/prompts/promptsGenerator.ts @@ -14,7 +14,7 @@ export class PromptsGenerator { ['aws.amazonq.fixCode', 'Fix'], ['aws.amazonq.optimizeCode', 'Optimize'], ['aws.amazonq.sendToPrompt', 'Send to prompt'], - ['aws.amazonq.testCode', 'Generate tests for'], + ['aws.amazonq.testCode', 'Generate unit tests for'], ]) public generateForContextMenuCommand(command: EditorContextCommand): string { From 4b19d4aac71218f5be33eb96d2555dc44a40443b Mon Sep 17 00:00:00 2001 From: Bodie Weedop Date: Wed, 11 Sep 2024 14:42:53 -0700 Subject: [PATCH 06/14] Revert removal of suppress prompts --- packages/amazonq/package.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index cb036b78246..bd8fdb14446 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -106,6 +106,14 @@ "amazonQWelcomePage": { "type": "boolean", "default": false + }, + "amazonQSessionConfigurationMessage": { + "type": "boolean", + "default": false + }, + "ssoCacheError": { + "type": "boolean", + "default": false } }, "additionalProperties": false From 4dc4f6bf56f24cedde4ec3d2df6d1e8ea3c1fce5 Mon Sep 17 00:00:00 2001 From: Bodie Weedop Date: Wed, 11 Sep 2024 15:38:44 -0700 Subject: [PATCH 07/14] Remove unnecessary imports --- .../core/src/codewhispererChat/controllers/chat/controller.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/src/codewhispererChat/controllers/chat/controller.ts b/packages/core/src/codewhispererChat/controllers/chat/controller.ts index 964ce4f46eb..ca3e9313c5b 100644 --- a/packages/core/src/codewhispererChat/controllers/chat/controller.ts +++ b/packages/core/src/codewhispererChat/controllers/chat/controller.ts @@ -2,7 +2,7 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ -import { Event as VSCodeEvent, Uri, workspace as VSCodeWorkspace, window as VSCodeWindow } from 'vscode' +import { Event as VSCodeEvent, Uri } from 'vscode' import { EditorContextExtractor } from '../../editor/context/extractor' import { ChatSessionStorage } from '../../storages/chatSession' import { Messenger, StaticTextResponseType } from './messenger/messenger' @@ -472,7 +472,6 @@ export class ChatController { type: 'chat_message', context, }) - return this.generateResponse( { message: message.message, From 17a9153f4428dcbc84e4579fb1359b06a4486be2 Mon Sep 17 00:00:00 2001 From: Bodie Weedop Date: Fri, 20 Sep 2024 17:24:50 -0700 Subject: [PATCH 08/14] Resolve circular dependency issue --- packages/amazonq/package.json | 8 +++---- packages/core/package.nls.json | 2 +- packages/core/src/auth/auth.ts | 21 +++++++++++++++++++ .../core/src/codewhisperer/util/authUtil.ts | 6 +----- .../commands/registerCommands.ts | 8 +++---- .../controllers/chat/messenger/messenger.ts | 2 +- .../chat/prompts/promptsGenerator.ts | 2 +- .../chat/userIntent/userIntentRecognizer.ts | 9 +++----- 8 files changed, 36 insertions(+), 22 deletions(-) diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index 033ec04dede..596242b5739 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -350,7 +350,7 @@ "group": "cw_chat@4" }, { - "command": "aws.amazonq.testCode", + "command": "aws.amazonq.generateUnitTests", "group": "cw_chat@5", "when": "aws.codewhisperer.connected && aws.isInternalUser" }, @@ -429,8 +429,8 @@ "enablement": "aws.codewhisperer.connected" }, { - "command": "aws.amazonq.testCode", - "title": "%AWS.command.amazonq.testCode%", + "command": "aws.amazonq.generateUnitTests", + "title": "%AWS.command.amazonq.generateUnitTests%", "category": "%AWS.amazonq.title%", "enablement": "aws.codewhisperer.connected && aws.isInternalUser" }, @@ -611,7 +611,7 @@ "linux": "meta+alt+q" }, { - "command": "aws.amazonq.testCode", + "command": "aws.amazonq.generateUnitTests", "key": "win+alt+t", "mac": "cmd+alt+t", "linux": "meta+alt+t", diff --git a/packages/core/package.nls.json b/packages/core/package.nls.json index 4fabf5a09b3..9458e7f7c58 100644 --- a/packages/core/package.nls.json +++ b/packages/core/package.nls.json @@ -111,7 +111,7 @@ "AWS.command.amazonq.fixCode": "Fix", "AWS.command.amazonq.optimizeCode": "Optimize", "AWS.command.amazonq.sendToPrompt": "Send to prompt", - "AWS.command.amazonq.testCode": "Test (Beta)", + "AWS.command.amazonq.generateUnitTests": "Generate Tests (Beta)", "AWS.command.amazonq.security.scan": "Run Project Scan", "AWS.command.deploySamApplication": "Deploy SAM Application", "AWS.command.aboutToolkit": "About", diff --git a/packages/core/src/auth/auth.ts b/packages/core/src/auth/auth.ts index a67368b6ffe..faadff7ecde 100644 --- a/packages/core/src/auth/auth.ts +++ b/packages/core/src/auth/auth.ts @@ -59,6 +59,7 @@ import { AwsConnection, scopesCodeWhispererCore, ProfileNotFoundError, + isSsoConnection, } from './connection' import { isSageMaker, isCloud9, isAmazonQ } from '../shared/extensionUtilities' import { telemetry } from '../shared/telemetry/telemetry' @@ -166,6 +167,26 @@ export class Auth implements AuthService, ConnectionManager { return this.#ssoCacheWatcher } + public get startUrl(): string | undefined { + // Reformat the url to remove any trailing '/' and `#` + // e.g. https://view.awsapps.com/start/# will become https://view.awsapps.com/start + return isSsoConnection(this.activeConnection) + ? this.reformatStartUrl(this.activeConnection.startUrl) + : undefined + } + + public isConnected(): boolean { + return this.activeConnection !== undefined + } + + public reformatStartUrl(startUrl: string | undefined) { + return !startUrl ? undefined : startUrl.replace(/[\/#]+$/g, '') + } + + public isInternalAmazonUser(): boolean { + return this.isConnected() && this.startUrl === 'https://amzn.awsapps.com/start' + } + /** * Map startUrl -> declared connections */ diff --git a/packages/core/src/codewhisperer/util/authUtil.ts b/packages/core/src/codewhisperer/util/authUtil.ts index 02a16b93dfe..137b669dd9e 100644 --- a/packages/core/src/codewhisperer/util/authUtil.ts +++ b/packages/core/src/codewhisperer/util/authUtil.ts @@ -151,7 +151,7 @@ export class AuthUtil { } await setContext('aws.codewhisperer.connected', this.isConnected()) - await setContext('aws.isInternalUser', this.isInternalAmazonUser()) + await setContext('aws.isInternalUser', Auth.instance.isInternalAmazonUser()) const doShowAmazonQLoginView = !this.isConnected() || this.isConnectionExpired() await setContext('aws.amazonq.showLoginView', doShowAmazonQLoginView) await setContext('aws.codewhisperer.connectionExpired', this.isConnectionExpired()) @@ -197,10 +197,6 @@ export class AuthUtil { return this.conn !== undefined && isBuilderIdConnection(this.conn) } - public isInternalAmazonUser(): boolean { - return this.isConnected() && this.startUrl === 'https://amzn.awsapps.com/start' - } - @withTelemetryContext({ name: 'connectToAwsBuilderId', class: authClassName }) public async connectToAwsBuilderId(): Promise { let conn = (await this.auth.listConnections()).find(isBuilderIdConnection) diff --git a/packages/core/src/codewhispererChat/commands/registerCommands.ts b/packages/core/src/codewhispererChat/commands/registerCommands.ts index 8ec0af9b02a..abf6c6fa07c 100644 --- a/packages/core/src/codewhispererChat/commands/registerCommands.ts +++ b/packages/core/src/codewhispererChat/commands/registerCommands.ts @@ -94,10 +94,10 @@ export function registerCommands(controllerPublishers: ChatControllerMessagePubl }) }) }) - Commands.register('aws.amazonq.testCode', async (data) => { - return focusAmazonQPanel.execute(placeholder, 'amazonq.testCode').then(() => { + Commands.register('aws.amazonq.generateUnitTests', async (data) => { + return focusAmazonQPanel.execute(placeholder, 'amazonq.generateUnitTests').then(() => { controllerPublishers.processContextMenuCommand.publish({ - type: 'aws.amazonq.testCode', + type: 'aws.amazonq.generateUnitTests', triggerType: getCommandTriggerType(data), }) }) @@ -110,7 +110,7 @@ export type EditorContextBaseCommandType = | 'aws.amazonq.fixCode' | 'aws.amazonq.optimizeCode' | 'aws.amazonq.sendToPrompt' - | 'aws.amazonq.testCode' + | 'aws.amazonq.generateUnitTests' export type CodeScanIssueCommandType = 'aws.amazonq.explainIssue' diff --git a/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts b/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts index fe3d8242218..3926cadb3d4 100644 --- a/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts +++ b/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts @@ -339,7 +339,7 @@ export class Messenger { ['aws.amazonq.fixCode', 'Fix'], ['aws.amazonq.optimizeCode', 'Optimize'], ['aws.amazonq.sendToPrompt', 'Send to prompt'], - ['aws.amazonq.testCode', 'Generate unit tests for'], + ['aws.amazonq.generateUnitTests', 'Generate unit tests for'], ]) public sendStaticTextResponse(type: StaticTextResponseType, triggerID: string, tabID: string) { diff --git a/packages/core/src/codewhispererChat/controllers/chat/prompts/promptsGenerator.ts b/packages/core/src/codewhispererChat/controllers/chat/prompts/promptsGenerator.ts index 06eeaa4ef8c..d738eddcb67 100644 --- a/packages/core/src/codewhispererChat/controllers/chat/prompts/promptsGenerator.ts +++ b/packages/core/src/codewhispererChat/controllers/chat/prompts/promptsGenerator.ts @@ -14,7 +14,7 @@ export class PromptsGenerator { ['aws.amazonq.fixCode', 'Fix'], ['aws.amazonq.optimizeCode', 'Optimize'], ['aws.amazonq.sendToPrompt', 'Send to prompt'], - ['aws.amazonq.testCode', 'Generate unit tests for'], + ['aws.amazonq.generateUnitTests', 'Generate unit tests for'], ]) public generateForContextMenuCommand(command: EditorContextCommand): string { diff --git a/packages/core/src/codewhispererChat/controllers/chat/userIntent/userIntentRecognizer.ts b/packages/core/src/codewhispererChat/controllers/chat/userIntent/userIntentRecognizer.ts index 5849e0a2ceb..32b6b274a51 100644 --- a/packages/core/src/codewhispererChat/controllers/chat/userIntent/userIntentRecognizer.ts +++ b/packages/core/src/codewhispererChat/controllers/chat/userIntent/userIntentRecognizer.ts @@ -6,7 +6,7 @@ import { UserIntent } from '@amzn/codewhisperer-streaming' import { EditorContextCommand } from '../../../commands/registerCommands' import { PromptMessage } from '../model' -import { AuthUtil } from '../../../../codewhisperer/util/authUtil' +import { Auth } from '../../../../auth' export class UserIntentRecognizer { public getFromContextMenuCommand(command: EditorContextCommand): UserIntent | undefined { @@ -19,7 +19,7 @@ export class UserIntentRecognizer { return UserIntent.APPLY_COMMON_BEST_PRACTICES case 'aws.amazonq.optimizeCode': return UserIntent.IMPROVE_CODE - case 'aws.amazonq.testCode': + case 'aws.amazonq.generateUnitTests': return UserIntent.GENERATE_UNIT_TESTS default: return undefined @@ -39,10 +39,7 @@ export class UserIntentRecognizer { return UserIntent.APPLY_COMMON_BEST_PRACTICES } else if (prompt.message.startsWith('Optimize')) { return UserIntent.IMPROVE_CODE - } else if ( - prompt.message.startsWith('Generate unit tests') && - AuthUtil.instance.startUrl === 'https://amzn.awsapps.com/start' - ) { + } else if (prompt.message.startsWith('Generate unit tests') && Auth.instance.isInternalAmazonUser()) { return UserIntent.GENERATE_UNIT_TESTS } return undefined From 5701cefcfb22c06ca59366654aeca33ddf4861f0 Mon Sep 17 00:00:00 2001 From: Bodie Weedop Date: Mon, 23 Sep 2024 16:15:44 -0700 Subject: [PATCH 09/14] Add generateUnitTests to telemetry --- .../src/codewhispererChat/controllers/chat/telemetryHelper.ts | 2 ++ packages/core/src/shared/telemetry/vscodeTelemetry.json | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/core/src/codewhispererChat/controllers/chat/telemetryHelper.ts b/packages/core/src/codewhispererChat/controllers/chat/telemetryHelper.ts index 036f0d9e155..11ded9b27cd 100644 --- a/packages/core/src/codewhispererChat/controllers/chat/telemetryHelper.ts +++ b/packages/core/src/codewhispererChat/controllers/chat/telemetryHelper.ts @@ -86,6 +86,8 @@ export class CWCTelemetryHelper { return 'explainLineByLine' case UserIntent.SHOW_EXAMPLES: return 'showExample' + case UserIntent.GENERATE_UNIT_TESTS: + return 'generateUnitTests' default: return undefined } diff --git a/packages/core/src/shared/telemetry/vscodeTelemetry.json b/packages/core/src/shared/telemetry/vscodeTelemetry.json index fffeec357b2..1f17a117960 100644 --- a/packages/core/src/shared/telemetry/vscodeTelemetry.json +++ b/packages/core/src/shared/telemetry/vscodeTelemetry.json @@ -58,7 +58,8 @@ "showExample", "citeSources", "explainLineByLine", - "explainCodeSelection" + "explainCodeSelection", + "generateUnitTests" ], "description": "Explict user intent associated with a chat message" }, From a2c7dc36cbb9d55948e52bdd5892806f32b83123 Mon Sep 17 00:00:00 2001 From: Bodie Weedop Date: Mon, 23 Sep 2024 16:16:51 -0700 Subject: [PATCH 10/14] Move check for internal Amazon user to general auth --- packages/core/src/auth/auth.ts | 3 +++ packages/core/src/codewhisperer/util/authUtil.ts | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/core/src/auth/auth.ts b/packages/core/src/auth/auth.ts index faadff7ecde..8393b52a3b3 100644 --- a/packages/core/src/auth/auth.ts +++ b/packages/core/src/auth/auth.ts @@ -67,6 +67,7 @@ import { randomUUID } from '../shared/crypto' import { asStringifiedStack } from '../shared/telemetry/spans' import { withTelemetryContext } from '../shared/telemetry/util' import { DiskCacheError } from '../shared/utilities/cacheUtils' +import { setContext } from '../shared/vscode/setContext' interface AuthService { /** @@ -244,6 +245,8 @@ export class Auth implements AuthService, ConnectionManager { this.#onDidChangeActiveConnection.fire(conn) await this.store.setCurrentProfileId(id) + await setContext('aws.isInternalUser', this.isInternalAmazonUser()) + return conn } diff --git a/packages/core/src/codewhisperer/util/authUtil.ts b/packages/core/src/codewhisperer/util/authUtil.ts index 137b669dd9e..c4d2c0109b8 100644 --- a/packages/core/src/codewhisperer/util/authUtil.ts +++ b/packages/core/src/codewhisperer/util/authUtil.ts @@ -151,7 +151,6 @@ export class AuthUtil { } await setContext('aws.codewhisperer.connected', this.isConnected()) - await setContext('aws.isInternalUser', Auth.instance.isInternalAmazonUser()) const doShowAmazonQLoginView = !this.isConnected() || this.isConnectionExpired() await setContext('aws.amazonq.showLoginView', doShowAmazonQLoginView) await setContext('aws.codewhisperer.connectionExpired', this.isConnectionExpired()) From 59a023b216467fc72c16ecbd1d9074ed6e7208db Mon Sep 17 00:00:00 2001 From: Bodie Weedop Date: Tue, 24 Sep 2024 09:40:55 -0700 Subject: [PATCH 11/14] Update naming of reformatStartUrl method Co-authored-by: Justin M. Keyes --- packages/core/src/auth/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/auth/auth.ts b/packages/core/src/auth/auth.ts index 17401ee496b..5a2c9588076 100644 --- a/packages/core/src/auth/auth.ts +++ b/packages/core/src/auth/auth.ts @@ -180,7 +180,7 @@ export class Auth implements AuthService, ConnectionManager { return this.activeConnection !== undefined } - public reformatStartUrl(startUrl: string | undefined) { + public normalizeStartUrl(startUrl: string | undefined) { return !startUrl ? undefined : startUrl.replace(/[\/#]+$/g, '') } From 9a11570e1ca07746d8ddc609462d073e718b00aa Mon Sep 17 00:00:00 2001 From: Bodie Weedop Date: Tue, 24 Sep 2024 14:22:56 -0700 Subject: [PATCH 12/14] Move comment to docstring --- packages/core/src/auth/auth.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/core/src/auth/auth.ts b/packages/core/src/auth/auth.ts index 5a2c9588076..27032251069 100644 --- a/packages/core/src/auth/auth.ts +++ b/packages/core/src/auth/auth.ts @@ -169,10 +169,8 @@ export class Auth implements AuthService, ConnectionManager { } public get startUrl(): string | undefined { - // Reformat the url to remove any trailing '/' and `#` - // e.g. https://view.awsapps.com/start/# will become https://view.awsapps.com/start return isSsoConnection(this.activeConnection) - ? this.reformatStartUrl(this.activeConnection.startUrl) + ? this.normalizeStartUrl(this.activeConnection.startUrl) : undefined } @@ -180,6 +178,12 @@ export class Auth implements AuthService, ConnectionManager { return this.activeConnection !== undefined } + /** + * Normalizes the provided URL + * + * Any trailing '/' and `#` is removed from the URL + * e.g. https://view.awsapps.com/start/# will become https://view.awsapps.com/start + */ public normalizeStartUrl(startUrl: string | undefined) { return !startUrl ? undefined : startUrl.replace(/[\/#]+$/g, '') } From a12768c56c31857c2599a9ca6f9dc9872e623847 Mon Sep 17 00:00:00 2001 From: Bodie Weedop Date: Tue, 24 Sep 2024 14:34:36 -0700 Subject: [PATCH 13/14] set context on forgotten and expired connection --- packages/core/src/auth/auth.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/core/src/auth/auth.ts b/packages/core/src/auth/auth.ts index 27032251069..eb726ab931b 100644 --- a/packages/core/src/auth/auth.ts +++ b/packages/core/src/auth/auth.ts @@ -401,6 +401,7 @@ export class Auth implements AuthService, ConnectionManager { } } this.#onDidDeleteConnection.fire({ connId, storedProfile: profile }) + await setContext('aws.isInternalUser', this.isInternalAmazonUser()) } @withTelemetryContext({ name: 'clearStaleLinkedIamConnections', class: authClassName }) @@ -433,6 +434,7 @@ export class Auth implements AuthService, ConnectionManager { await provider.invalidate('devModeManualExpiration') // updates the state of the connection await this.refreshConnectionState(conn) + await setContext('aws.isInternalUser', this.isInternalAmazonUser()) } public async getConnection(connection: Pick): Promise { From 12f879d187622cdf75e0e0df5af598673b5e80a2 Mon Sep 17 00:00:00 2001 From: Bodie Weedop Date: Wed, 25 Sep 2024 09:34:28 -0700 Subject: [PATCH 14/14] Default isInternalUser to false on forgotten and expired connection --- packages/core/src/auth/auth.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/auth/auth.ts b/packages/core/src/auth/auth.ts index eb726ab931b..d7c2a5d58b7 100644 --- a/packages/core/src/auth/auth.ts +++ b/packages/core/src/auth/auth.ts @@ -401,7 +401,7 @@ export class Auth implements AuthService, ConnectionManager { } } this.#onDidDeleteConnection.fire({ connId, storedProfile: profile }) - await setContext('aws.isInternalUser', this.isInternalAmazonUser()) + await setContext('aws.isInternalUser', false) } @withTelemetryContext({ name: 'clearStaleLinkedIamConnections', class: authClassName }) @@ -434,7 +434,7 @@ export class Auth implements AuthService, ConnectionManager { await provider.invalidate('devModeManualExpiration') // updates the state of the connection await this.refreshConnectionState(conn) - await setContext('aws.isInternalUser', this.isInternalAmazonUser()) + await setContext('aws.isInternalUser', false) } public async getConnection(connection: Pick): Promise {