diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000000..c5faaa009d6 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["amodio.tsl-problem-matcher", "dbaeumer.vscode-eslint"] +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 358eb4860fa..51eba829681 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,7 +29,8 @@ To develop this project, install these dependencies: - [Git](https://git-scm.com/downloads) - (optional) Set `git blame` to ignore noise-commits: `git config blame.ignoreRevsFile .git-blame-ignore-revs` - [AWS `git secrets`](https://github.com/awslabs/git-secrets) -- (required for Web mode) [TypeScript + Webpack Problem Matcher](https://marketplace.visualstudio.com/items?itemName=amodio.tsl-problem-matcher) +- [TypeScript + Webpack Problem Matcher](https://marketplace.visualstudio.com/items?itemName=amodio.tsl-problem-matcher) + - Not installing will result in the following error during building: `Error: Invalid problemMatcher reference: $ts-webpack-watch` - (optional) [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) - (optional) [Docker](https://docs.docker.com/get-docker/) diff --git a/packages/amazonq/.vscode/extensions.json b/packages/amazonq/.vscode/extensions.json deleted file mode 100644 index b1a2d99f09e..00000000000 --- a/packages/amazonq/.vscode/extensions.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": ["dbaeumer.vscode-eslint"] -} diff --git a/packages/core/.vscode/extensions.json b/packages/core/.vscode/extensions.json deleted file mode 100644 index b1a2d99f09e..00000000000 --- a/packages/core/.vscode/extensions.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": ["dbaeumer.vscode-eslint"] -} diff --git a/packages/core/src/amazonqFeatureDev/storages/chatSession.ts b/packages/core/src/amazonqFeatureDev/storages/chatSession.ts index 02093089084..bbf6a6086d4 100644 --- a/packages/core/src/amazonqFeatureDev/storages/chatSession.ts +++ b/packages/core/src/amazonqFeatureDev/storages/chatSession.ts @@ -3,11 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ +import AsyncLock from 'async-lock' import { Messenger } from '../controllers/chat/messenger/messenger' import { Session } from '../session/session' import { createSessionConfig } from '../session/sessionConfigFactory' export class ChatSessionStorage { + private lock = new AsyncLock() + private sessions: Map = new Map() constructor(private readonly messenger: Messenger) {} @@ -20,12 +23,19 @@ export class ChatSessionStorage { } public async getSession(tabID: string): Promise { - const sessionFromStorage = this.sessions.get(tabID) - if (sessionFromStorage === undefined) { - // If a session doesn't already exist just create it - return this.createSession(tabID) - } - return sessionFromStorage + /** + * The lock here is added in order to mitigate amazon Q's eventing fire & forget design when integrating with mynah-ui that creates a race condition here. + * The race condition happens when handleDevFeatureCommand in src/amazonq/webview/ui/quickActions/handler.ts is firing two events after each other to amazonqFeatureDev controller + * This eventually may make code generation fail as at the moment of that event it may get from the storage a session that has not been properly updated. + */ + return this.lock.acquire(tabID, async () => { + const sessionFromStorage = this.sessions.get(tabID) + if (sessionFromStorage === undefined) { + // If a session doesn't already exist just create it + return this.createSession(tabID) + } + return sessionFromStorage + }) } // Find all sessions that are currently waiting to be authenticated diff --git a/packages/core/src/amazonqGumby/activation.ts b/packages/core/src/amazonqGumby/activation.ts index ac88becd152..b52e520160d 100644 --- a/packages/core/src/amazonqGumby/activation.ts +++ b/packages/core/src/amazonqGumby/activation.ts @@ -9,7 +9,6 @@ import { TransformationHubViewProvider } from '../codewhisperer/service/transfor import { ExtContext } from '../shared/extensions' import { stopTransformByQ } from '../codewhisperer/commands/startTransformByQ' import { transformByQState } from '../codewhisperer/models/model' -import * as CodeWhispererConstants from '../codewhisperer/models/constants' import { ProposedTransformationExplorer } from '../codewhisperer/service/transformByQ/transformationResultsViewProvider' import { codeTransformTelemetryState } from './telemetry/codeTransformTelemetryState' import { telemetry } from '../shared/telemetry/telemetry' @@ -52,8 +51,6 @@ export async function activate(context: ExtContext) { Commands.register('aws.amazonq.stopTransformationInHub', async (cancelSrc: CancelActionPositions) => { if (transformByQState.isRunning()) { void stopTransformByQ(transformByQState.getJobId(), cancelSrc) - } else { - void vscode.window.showInformationMessage(CodeWhispererConstants.noOngoingJobMessage) } }), diff --git a/packages/core/src/amazonqGumby/chat/controller/controller.ts b/packages/core/src/amazonqGumby/chat/controller/controller.ts index 7c522124620..2b181403cd9 100644 --- a/packages/core/src/amazonqGumby/chat/controller/controller.ts +++ b/packages/core/src/amazonqGumby/chat/controller/controller.ts @@ -24,6 +24,7 @@ import { validateCanCompileProject, } from '../../../codewhisperer/commands/startTransformByQ' import { JDKVersion, TransformationCandidateProject, transformByQState } from '../../../codewhisperer/models/model' +import * as CodeWhispererConstants from '../../../codewhisperer/models/constants' import { JavaHomeNotSetError, NoJavaProjectsFoundError, NoMavenJavaProjectsFoundError } from '../../errors' import MessengerUtils, { ButtonActions, GumbyCommands } from './messenger/messengerUtils' import { CancelActionPositions } from '../../telemetry/codeTransformTelemetry' @@ -213,7 +214,7 @@ export class GumbyController { await this.initiateTransformationOnProject(message) break case ButtonActions.CANCEL_TRANSFORMATION_FORM: - this.messenger.sendJobFinishedMessage(message.tabId, true) + this.messenger.sendJobFinishedMessage(message.tabId, CodeWhispererConstants.jobCancelledChatMessage) break case ButtonActions.VIEW_TRANSFORMATION_HUB: await vscode.commands.executeCommand(GumbyCommands.FOCUS_TRANSFORMATION_HUB) @@ -309,10 +310,10 @@ export class GumbyController { await this.prepareProjectForSubmission(message) } - private async transformationFinished(tabID: string, jobStatus: string = '') { + private async transformationFinished(data: { message: string; tabID: string }) { this.sessionStorage.getSession().conversationState = ConversationState.IDLE // at this point job is either completed, partially_completed, cancelled, or failed - this.messenger.sendJobFinishedMessage(tabID, false) + this.messenger.sendJobFinishedMessage(data.tabID, data.message) } private async processHumanChatMessage(data: { message: string; tabID: string }) { @@ -346,6 +347,7 @@ export class GumbyController { * Examples: * ``` * extractPath("./some/path/here") => "C:/some/root/some/path/here" + * extractPath(" ./some/path/here\n") => "C:/some/root/some/path/here" * extractPath("C:/some/nonexistent/path/here") => undefined * extractPath("C:/some/filepath/.txt") => undefined * ``` @@ -354,6 +356,6 @@ export class GumbyController { * @returns the absolute path if path points to existing folder, otherwise undefined */ function extractPath(text: string): string | undefined { - const resolvedPath = path.resolve(text) + const resolvedPath = path.resolve(text.trim()) return fs.existsSync(resolvedPath) && fs.lstatSync(resolvedPath).isDirectory() ? resolvedPath : undefined } diff --git a/packages/core/src/amazonqGumby/chat/controller/messenger/messenger.ts b/packages/core/src/amazonqGumby/chat/controller/messenger/messenger.ts index 05b13ebe970..8bd9ca0e753 100644 --- a/packages/core/src/amazonqGumby/chat/controller/messenger/messenger.ts +++ b/packages/core/src/amazonqGumby/chat/controller/messenger/messenger.ts @@ -12,6 +12,7 @@ import { AuthFollowUpType, expiredText, enableQText, reauthenticateText } from ' import { ChatItemType } from '../../../../amazonqFeatureDev/models' import { JDKVersion, TransformationCandidateProject } from '../../../../codewhisperer/models/model' import { FeatureAuthState } from '../../../../codewhisperer/util/authUtil' +import * as CodeWhispererConstants from '../../../../codewhisperer/models/constants' import { AppToWebViewMessageDispatcher, AsyncEventProgressMessage, @@ -35,7 +36,6 @@ export type StaticTextResponseType = export type ErrorTextResponseType = | 'no-project-found' - | 'no-workspace-open' | 'no-java-project-found' | 'no-maven-java-project-found' | 'could-not-compile-project' @@ -65,7 +65,7 @@ export class Messenger { public sendErrorMessage(errorMessage: string, tabID: string) { this.dispatcher.sendErrorMessage( - new ErrorMessage(`Sorry, we encountered a problem when processing your request.`, errorMessage, tabID) + new ErrorMessage(CodeWhispererConstants.genericErrorMessage, errorMessage, tabID) ) } @@ -195,7 +195,7 @@ export class Messenger { } public sendCompilationInProgress(tabID: string) { - const message = `I'm building your project. This can take up to 10 minutes, depending on the size of your project.` + const message = CodeWhispererConstants.buildStartedChatMessage this.dispatcher.sendAsyncEventProgress( new AsyncEventProgressMessage(tabID, { inProgress: true, message: undefined }) @@ -210,7 +210,7 @@ export class Messenger { } public sendCompilationFinished(tabID: string) { - const message = `I was able to build your project. I'll start transforming your code soon.` + const message = CodeWhispererConstants.buildSucceededChatMessage this.dispatcher.sendAsyncEventProgress( new AsyncEventProgressMessage(tabID, { @@ -221,23 +221,22 @@ export class Messenger { } public sendJobSubmittedMessage(tabID: string, disableJobActions: boolean = false) { - const message = `I'm starting to transform your code. It can take 10 to 30 minutes to upgrade your code, depending on the size of your project. To monitor progress, go to the Transformation Hub.` + const message = CodeWhispererConstants.jobStartedChatMessage const buttons: ChatItemButton[] = [] if (!disableJobActions) { // Note: buttons can only be clicked once. - // To get around this, we remove the card after they're clicked and then - // resubmit the message. + // To get around this, we remove the card after it's clicked and then resubmit the message. buttons.push({ keepCardAfterClick: true, - text: 'Open Transformation Hub', + text: CodeWhispererConstants.openTransformationHubButtonText, id: ButtonActions.VIEW_TRANSFORMATION_HUB, }) buttons.push({ keepCardAfterClick: true, - text: 'Stop transformation', + text: CodeWhispererConstants.stopTransformationButtonText, id: ButtonActions.STOP_TRANSFORMATION_JOB, }) } @@ -291,40 +290,29 @@ export class Messenger { let message = '...' switch (type) { - case 'no-workspace-open': - message = 'To begin, please open a workspace.' - break case 'no-project-found': - message = `Sorry, I couldn't find any open projects. Currently, I can only upgrade Java projects built on Maven. - -For more information, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html).` + message = CodeWhispererConstants.noOpenProjectsFoundChatMessage break case 'no-java-project-found': - message = `Sorry, I can't upgrade any of your open projects. Currently, I can only upgrade Java projects built on Maven. - -For more information, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html).` + message = CodeWhispererConstants.noJavaProjectsFoundChatMessage break case 'no-maven-java-project-found': - message = `Sorry, I can't upgrade any of your open projects. I couldn't find a pom.xml file in any of your Java projects. Currently, I can only upgrade Java projects built on Maven. - -For more information, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html).` + message = CodeWhispererConstants.noPomXmlFoundChatMessage break case 'could-not-compile-project': - message = `Sorry, I couldn't run Maven clean install to build your project. To troubleshoot, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html#maven-commands-failing).` + message = CodeWhispererConstants.cleanInstallErrorChatMessage break case 'invalid-java-home': - message = `Sorry, I couldn't locate your Java installation. To troubleshoot, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html#maven-commands-failing).` + message = CodeWhispererConstants.noJavaHomeFoundChatMessage break case 'unsupported-source-jdk-version': - message = `Sorry, currently I can only upgrade Java 8 or Java 11 projects. - -For more information, see the [Amazon Q documentation.](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html).` + message = CodeWhispererConstants.unsupportedJavaVersionChatMessage } const buttons: ChatItemButton[] = [] buttons.push({ keepCardAfterClick: false, - text: 'Start a new transformation', + text: CodeWhispererConstants.startTransformationButtonText, id: ButtonActions.CONFIRM_START_TRANSFORMATION_FLOW, }) @@ -344,19 +332,11 @@ For more information, see the [Amazon Q documentation.](https://docs.aws.amazon. this.dispatcher.sendCommandMessage(new SendCommandMessage(message.command, message.tabId, message.eventId)) } - public sendJobFinishedMessage(tabID: string, cancelled: boolean, jobStatus: string = '') { - let message = - 'I cancelled your transformation. If you want to start another transformation, choose **Start a new transformation**.' - - if (!cancelled) { - message = - 'The transformation job is over. If you want to start another transformation, choose **Start a new transformation**.' - } - + public sendJobFinishedMessage(tabID: string, message: string = '') { const buttons: ChatItemButton[] = [] buttons.push({ keepCardAfterClick: false, - text: 'Start a new transformation', + text: CodeWhispererConstants.startTransformationButtonText, id: ButtonActions.CONFIRM_START_TRANSFORMATION_FLOW, }) @@ -380,7 +360,7 @@ For more information, see the [Amazon Q documentation.](https://docs.aws.amazon. this.dispatcher.sendAsyncEventProgress( new AsyncEventProgressMessage(tabID, { inProgress: true, - message: "I'm checking for open projects that are eligible for Code Transformation.", + message: CodeWhispererConstants.checkingForProjectsChatMessage, }) ) } diff --git a/packages/core/src/amazonqGumby/chat/controller/messenger/messengerUtils.ts b/packages/core/src/amazonqGumby/chat/controller/messenger/messengerUtils.ts index 97bdf4411f4..b499e658f80 100644 --- a/packages/core/src/amazonqGumby/chat/controller/messenger/messengerUtils.ts +++ b/packages/core/src/amazonqGumby/chat/controller/messenger/messengerUtils.ts @@ -6,12 +6,7 @@ import * as os from 'os' import { transformByQState, JDKVersion } from '../../../../codewhisperer/models/model' -import { - enterJavaHomeMessage, - nonWindowsJava11HomeHelpMessage, - nonWindowsJava8HomeHelpMessage, - windowsJavaHomeHelpMessage, -} from './stringConstants' +import * as CodeWhispererConstants from '../../../../codewhisperer/models/constants' // These enums map to string IDs export enum ButtonActions { @@ -32,18 +27,20 @@ export enum GumbyCommands { export default class MessengerUtils { static createJavaHomePrompt = (): string => { - let javaHomePrompt = `${enterJavaHomeMessage} ${transformByQState.getSourceJDKVersion()}. \n` + let javaHomePrompt = `${ + CodeWhispererConstants.enterJavaHomeChatMessage + } ${transformByQState.getSourceJDKVersion()}. \n` if (os.platform() === 'win32') { - javaHomePrompt += windowsJavaHomeHelpMessage.replace( + javaHomePrompt += CodeWhispererConstants.windowsJavaHomeHelpChatMessage.replace( 'JAVA_VERSION_HERE', transformByQState.getSourceJDKVersion()! ) } else { const jdkVersion = transformByQState.getSourceJDKVersion() if (jdkVersion === JDKVersion.JDK8) { - javaHomePrompt += ` ${nonWindowsJava8HomeHelpMessage}` + javaHomePrompt += ` ${CodeWhispererConstants.nonWindowsJava8HomeHelpChatMessage}` } else if (jdkVersion === JDKVersion.JDK11) { - javaHomePrompt += ` ${nonWindowsJava11HomeHelpMessage}` + javaHomePrompt += ` ${CodeWhispererConstants.nonWindowsJava11HomeHelpChatMessage}` } } return javaHomePrompt @@ -77,6 +74,6 @@ export default class MessengerUtils { } } - return `I can upgrade your ${javaVersionString}. To start the transformation, I need some information from you. Choose the project you want to upgrade and the target code version to upgrade to. Then, choose Transform.` + return CodeWhispererConstants.projectPromptChatMessage.replace('JAVA_VERSION_HERE', javaVersionString) } } diff --git a/packages/core/src/amazonqGumby/chat/controller/messenger/stringConstants.ts b/packages/core/src/amazonqGumby/chat/controller/messenger/stringConstants.ts deleted file mode 100644 index 75d0926abc7..00000000000 --- a/packages/core/src/amazonqGumby/chat/controller/messenger/stringConstants.ts +++ /dev/null @@ -1,23 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - * - */ - -export const enterJavaHomeMessage = 'Enter the path to JDK ' - -export const windowsJavaHomeHelpMessage = - 'To find the JAVA_HOME path, run the following command in a new IDE terminal: `cd "C:\\Program Files\\Java" && dir`. If you see your JDK version, run `cd ` and then `cd` to show the path.' - -export const nonWindowsJava8HomeHelpMessage = - 'To find the JAVA_HOME path, run the following command in a new IDE terminal: `/usr/libexec/java_home -v 1.8`' - -export const nonWindowsJava11HomeHelpMessage = - 'To find the JAVA_HOME path, run the following command in a new IDE terminal: `/usr/libexec/java_home -v 11`' - -export const projectSizeTooLargeMessage = - 'Your project size exceeds the Amazon Q Code Transformation upload limit of 1GB. For more information, see the [Code Transformation documentation](LINK_HERE).' - -export const JDK8VersionNumber = '52' - -export const JDK11VersionNumber = '55' diff --git a/packages/core/src/codewhisperer/commands/startTransformByQ.ts b/packages/core/src/codewhisperer/commands/startTransformByQ.ts index f05729db2a0..453c8b27e84 100644 --- a/packages/core/src/codewhisperer/commands/startTransformByQ.ts +++ b/packages/core/src/codewhisperer/commands/startTransformByQ.ts @@ -4,7 +4,6 @@ */ import * as vscode from 'vscode' -import * as nls from 'vscode-nls' import * as fs from 'fs' import * as os from 'os' import path from 'path' @@ -53,9 +52,6 @@ import { ChatSessionManager } from '../../amazonqGumby/chat/storages/chatSession import { getDependenciesFolderInfo, writeLogs } from '../service/transformByQ/transformFileHandler' import { sleep } from '../../shared/utilities/timeoutUtils' -const localize = nls.loadMessageBundle() -export const stopTransformByQButton = localize('aws.codewhisperer.stop.transform.by.q', 'Stop') - let sessionJobHistory: { timestamp: string; module: string; status: string; duration: string; id: string }[] = [] export async function startTransformByQWithProgress() { @@ -164,7 +160,6 @@ export async function startTransformByQ() { const status = await pollTransformationStatusUntilComplete(jobId) // Set the result state variables for our store and the UI - // At this point job should be completed or partially completed await finalizeTransformationJob(status) } catch (error: any) { await transformationJobErrorHandler(error) @@ -177,6 +172,8 @@ export async function startTransformByQ() { export async function preTransformationUploadCode() { await vscode.commands.executeCommand('aws.amazonq.transformationHub.focus') + void vscode.window.showInformationMessage(CodeWhispererConstants.jobStartedNotification) + let uploadId = '' let payloadFilePath = '' throwIfCancelled() @@ -186,6 +183,8 @@ export async function preTransformationUploadCode() { uploadId = await uploadPayload(payloadFilePath) } catch (err) { const errorMessage = `Failed to upload code due to ${(err as Error).message}` + transformByQState.setJobFailureErrorNotification(CodeWhispererConstants.failedToUploadProjectNotification) + transformByQState.setJobFailureErrorChatMessage(CodeWhispererConstants.failedToUploadProjectChatMessage) getLogger().error(errorMessage) throw err } @@ -201,8 +200,18 @@ export async function startTransformationJob(uploadId: string) { try { jobId = await startJob(uploadId) } catch (error) { - const errorMessage = CodeWhispererConstants.failedToStartJobMessage - transformByQState.setJobFailureErrorMessage(errorMessage) + getLogger().error(`CodeTransformation: ${CodeWhispererConstants.failedToStartJobNotification}`, error) + if ((error as Error).message.includes('too many active running jobs')) { + transformByQState.setJobFailureErrorNotification( + CodeWhispererConstants.failedToStartJobTooManyJobsNotification + ) + transformByQState.setJobFailureErrorChatMessage( + CodeWhispererConstants.failedToStartJobTooManyJobsChatMessage + ) + } else { + transformByQState.setJobFailureErrorNotification(CodeWhispererConstants.failedToStartJobNotification) + transformByQState.setJobFailureErrorChatMessage(CodeWhispererConstants.failedToStartJobChatMessage) + } throw new Error('Start job failed') } transformByQState.setJobId(encodeHTML(jobId)) @@ -217,18 +226,18 @@ export async function pollTransformationStatusUntilPlanReady(jobId: string) { try { await pollTransformationJob(jobId, CodeWhispererConstants.validStatesForPlanGenerated) } catch (error) { - const errorMessage = CodeWhispererConstants.failedToCompleteJobMessage - getLogger().error(`CodeTransformation: ${errorMessage}`, error) - transformByQState.setJobFailureErrorMessage(errorMessage) + getLogger().error(`CodeTransformation: ${CodeWhispererConstants.failedToCompleteJobNotification}`, error) + transformByQState.setJobFailureErrorNotification(CodeWhispererConstants.failedToCompleteJobNotification) + transformByQState.setJobFailureErrorChatMessage(CodeWhispererConstants.failedToCompleteJobChatMessage) throw new Error('Poll job failed') } let plan = undefined try { plan = await getTransformationPlan(jobId) } catch (error) { - const errorMessage = CodeWhispererConstants.failedToCompleteJobMessage - getLogger().error(`CodeTransformation: ${errorMessage}`, error) - transformByQState.setJobFailureErrorMessage(errorMessage) + getLogger().error(`CodeTransformation: ${CodeWhispererConstants.failedToCompleteJobNotification}`, error) + transformByQState.setJobFailureErrorNotification(CodeWhispererConstants.failedToGetPlanNotification) + transformByQState.setJobFailureErrorChatMessage(CodeWhispererConstants.failedToGetPlanChatMessage) throw new Error('Get plan failed') } @@ -246,9 +255,9 @@ export async function pollTransformationStatusUntilComplete(jobId: string) { try { status = await pollTransformationJob(jobId, CodeWhispererConstants.validStatesForCheckingDownloadUrl) } catch (error) { - const errorMessage = CodeWhispererConstants.failedToCompleteJobMessage - getLogger().error(`CodeTransformation: ${errorMessage}`, error) - transformByQState.setJobFailureErrorMessage(errorMessage) + getLogger().error(`CodeTransformation: ${CodeWhispererConstants.failedToCompleteJobNotification}`, error) + transformByQState.setJobFailureErrorNotification(CodeWhispererConstants.failedToCompleteJobNotification) + transformByQState.setJobFailureErrorChatMessage(CodeWhispererConstants.failedToCompleteJobChatMessage) throw new Error('Poll job failed') } @@ -257,10 +266,10 @@ export async function pollTransformationStatusUntilComplete(jobId: string) { export async function finalizeTransformationJob(status: string) { if (!(status === 'COMPLETED' || status === 'PARTIALLY_COMPLETED')) { - const errorMessage = CodeWhispererConstants.failedToCompleteJobMessage - getLogger().error(`CodeTransformation: ${errorMessage}`) + getLogger().error(`CodeTransformation: ${CodeWhispererConstants.failedToCompleteJobNotification}`) sessionPlanProgress['transformCode'] = StepProgress.Failed - transformByQState.setJobFailureErrorMessage(errorMessage) + transformByQState.setJobFailureErrorNotification(CodeWhispererConstants.failedToCompleteJobNotification) + transformByQState.setJobFailureErrorChatMessage(CodeWhispererConstants.failedToCompleteJobChatMessage) throw new Error('Job was not successful nor partially successful') } @@ -332,7 +341,16 @@ export async function postTransformationJob() { sessionPlanProgress['transformCode'] = StepProgress.Failed } - transformByQState.getChatControllers()?.transformationFinished.fire(ChatSessionManager.Instance.getSession().tabID) + let chatMessage = transformByQState.getJobFailureErrorChatMessage() + if (transformByQState.isSucceeded()) { + chatMessage = CodeWhispererConstants.jobCompletedChatMessage + } else if (transformByQState.isPartiallySucceeded()) { + chatMessage = CodeWhispererConstants.jobPartiallyCompletedChatMessage + } + + transformByQState + .getChatControllers() + ?.transformationFinished.fire({ message: chatMessage, tabID: ChatSessionManager.Instance.getSession().tabID }) const durationInMs = calculateTotalLatency(codeTransformTelemetryState.getStartTime()) const resultStatusMessage = codeTransformTelemetryState.getResultStatus() @@ -361,11 +379,11 @@ export async function postTransformationJob() { ) if (transformByQState.isSucceeded()) { - void vscode.window.showInformationMessage(CodeWhispererConstants.transformByQCompletedMessage) + void vscode.window.showInformationMessage(CodeWhispererConstants.jobCompletedNotification) } else if (transformByQState.isPartiallySucceeded()) { void vscode.window .showInformationMessage( - CodeWhispererConstants.transformByQPartiallyCompletedMessage, + CodeWhispererConstants.jobPartiallyCompletedNotification, CodeWhispererConstants.amazonQFeedbackText ) .then(choice => { @@ -385,9 +403,13 @@ export async function transformationJobErrorHandler(error: any) { // means some other error occurred; cancellation already handled by now with stopTransformByQ transformByQState.setToFailed() codeTransformTelemetryState.setResultStatus('JobFailed') - let displayedErrorMessage = transformByQState.getJobFailureErrorMessage() + // jobFailureErrorNotification should always be defined here + let displayedErrorMessage = transformByQState.getJobFailureErrorNotification() ?? 'Job failed' if (transformByQState.getJobFailureMetadata() !== '') { displayedErrorMessage += ` ${transformByQState.getJobFailureMetadata()}` + transformByQState.setJobFailureErrorChatMessage( + `${transformByQState.getJobFailureErrorChatMessage()} ${transformByQState.getJobFailureMetadata()}` + ) } void vscode.window .showErrorMessage(displayedErrorMessage, CodeWhispererConstants.amazonQFeedbackText) @@ -396,6 +418,8 @@ export async function transformationJobErrorHandler(error: any) { void submitFeedback(placeholder, CodeWhispererConstants.amazonQFeedbackKey) } }) + } else { + transformByQState.setJobFailureErrorChatMessage(CodeWhispererConstants.jobCancelledChatMessage) } getLogger().error(`CodeTransformation: ${error.message}`) } @@ -445,7 +469,7 @@ export async function stopTransformByQ( await stopJob(jobId) void vscode.window .showErrorMessage( - CodeWhispererConstants.transformByQCancelledMessage, + CodeWhispererConstants.jobCancelledNotification, CodeWhispererConstants.amazonQFeedbackText ) .then(choice => { @@ -456,7 +480,7 @@ export async function stopTransformByQ( } catch { void vscode.window .showErrorMessage( - CodeWhispererConstants.errorStoppingJobMessage, + CodeWhispererConstants.errorStoppingJobNotification, CodeWhispererConstants.amazonQFeedbackText ) .then(choice => { diff --git a/packages/core/src/codewhisperer/models/constants.ts b/packages/core/src/codewhisperer/models/constants.ts index c8246c0555a..4095d8cdc9c 100644 --- a/packages/core/src/codewhisperer/models/constants.ts +++ b/packages/core/src/codewhisperer/models/constants.ts @@ -271,164 +271,228 @@ export const newCustomizationMessage = 'You have access to new Amazon Q customiz export const newCustomizationsAvailableKey = 'aws.amazonq.codewhisperer.newCustomizations' +// Start of QCT Strings + +export const uploadZipSizeLimitInBytes = 1000000000 // 1GB + +export const maxBufferSize = 1024 * 1024 * 8 // this is 8MB; the default max buffer size for stdout for spawnSync is 1MB + +export const transformationJobPollingIntervalSeconds = 5 + +export const transformationJobTimeoutSeconds = 60 * 60 // 1 hour, to match backend + +export const defaultLanguage = 'Java' + +export const contentChecksumType = 'SHA_256' + +export const uploadIntent = 'TRANSFORMATION' + +export const transformationType = 'LANGUAGE_UPGRADE' + +// job successfully started +export const validStatesForJobStarted = [ + 'STARTED', + 'PREPARING', + 'PREPARED', + 'PLANNING', + 'PLANNED', + 'TRANSFORMING', + 'TRANSFORMED', +] + +// initial build succeeded +export const validStatesForBuildSucceeded = ['PREPARED', 'PLANNING', 'PLANNED', 'TRANSFORMING', 'TRANSFORMED'] + +// plan must be available +export const validStatesForPlanGenerated = ['PLANNED', 'TRANSFORMING', 'TRANSFORMED'] + +export const failureStates = ['FAILED', 'STOPPING', 'STOPPED', 'REJECTED'] + +// if status is COMPLETED or PARTIALLY_COMPLETED we can download artifacts +export const validStatesForCheckingDownloadUrl = [ + 'COMPLETED', + 'PARTIALLY_COMPLETED', + 'FAILED', + 'STOPPING', + 'STOPPED', + 'REJECTED', +] + export const amazonQDismissedKey = 'aws.toolkit.amazonq.dismissed' export const amazonQInstallDismissedKey = 'aws.toolkit.amazonqInstall.dismissed' -// Amazon Q Code Transformation - export const amazonQFeedbackKey = 'Amazon Q' export const amazonQFeedbackText = 'Submit feedback' -export const selectProjectPrompt = 'Select the project you want to transform' +export const jobStartedChatMessage = + "I'm starting to transform your code. It can take 10 to 30 minutes to upgrade your code, depending on the size of your project. To monitor progress, go to the Transformation Hub." + +export const jobStartedNotification = + 'Amazon Q is transforming your code. It can take 10 to 30 minutes to upgrade your code, depending on the size of your project. To monitor progress, go to the Transformation Hub.' -export const compilingProjectMessage = 'Amazon Q is compiling your project. This can take up to 10 minutes.' +export const openTransformationHubButtonText = 'Open Transformation Hub' -export const submittedProjectMessage = - 'Your project has been submitted for transformation. The code transformation process may take 10-30 mins depending on the size of your project.' +export const startTransformationButtonText = 'Start a new transformation' -export const unsupportedJavaVersionSelectedMessage = - 'None of your open projects are supported by Amazon Q Code Transformation. For more information, see the [Amazon Q documentation](LINK_HERE).' +export const stopTransformationButtonText = 'Stop transformation' -export const transformByQWindowTitle = 'Amazon Q Code Transformation' +export const checkingForProjectsChatMessage = + "I'm checking for open projects that are eligible for Code Transformation." -export const failedToStartJobMessage = - "Amazon Q couldn't begin the transformation. Try starting the transformation again." +export const buildStartedChatMessage = + "I'm building your project. This can take up to 10 minutes, depending on the size of your project." -export const failedToCompleteJobMessage = - "Amazon Q couldn't complete the transformation. Try starting the transformation again." +export const buildSucceededChatMessage = 'I was able to build your project and will start transforming your code soon.' -export const stopTransformByQMessage = 'Stop transformation?' +export const buildSucceededNotification = + 'Amazon Q was able to build your project and will start transforming your code soon.' -export const stoppingTransformByQMessage = 'Stopping transformation...' +export const unsupportedJavaVersionChatMessage = + 'Sorry, currently I can only upgrade Java 8 or Java 11 projects. For more information, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/code-transformation.html#prerequisites).' -export const transformByQCancelledMessage = 'You cancelled the transformation.' +export const failedToStartJobChatMessage = + "Sorry, I couldn't begin the transformation. Please try starting the transformation again." -export const transformByQCompletedMessage = - 'Amazon Q successfully transformed your code. You can download a summary of the transformation and a diff with proposed changes in the Transformation Hub.' +export const failedToStartJobNotification = + "Amazon Q couldn't begin the transformation. Please try starting the transformation again." -export const transformByQPartiallyCompletedMessage = - 'Amazon Q partially transformed your code. You can download a summary of the transformation and a diff with proposed changes in the Transformation Hub.' +export const failedToStartJobTooManyJobsChatMessage = + 'Sorry, I couldn’t begin the transformation. You have too many active transformations running. Please try again after your other transformations have completed.' -export const noPomXmlFoundMessage = - 'None of your open Java projects are supported by Amazon Q Code Transformation. Currently, Amazon Q can only upgrade Java projects built on Maven. A pom.xml must be present in the root of your project to upgrade it. For more information, see the [Amazon Q documentation](LINK_HERE).' +export const failedToStartJobTooManyJobsNotification = + "Amazon Q couldn't begin the transformation. You have too many active transformations running. Please try again after your other transformations have completed." -export const noActiveIdCMessage = - 'Amazon Q Code Transformation requires an active IAM Identity Center connection. For more information, see the [Code Transformation documentation](LINK_HERE).' +export const failedToUploadProjectChatMessage = + "Sorry, I couldn't upload your project. Please try starting the transformation again." export const noOngoingJobMessage = 'No job is in-progress at the moment' -export const jobInProgressMessage = 'Job is already in-progress' +export const failedToUploadProjectNotification = + "Amazon Q couldn't upload your project. Please try starting the transformation again." -export const cancellationInProgressMessage = 'Cancellation is in-progress' +export const failedToGetPlanChatMessage = + "Sorry, I couldn't create the transformation plan to upgrade your project. Please try starting the transformation again." -export const errorStoppingJobMessage = "Amazon Q couldn't stop the transformation." +export const failedToGetPlanNotification = + "Amazon Q couldn't create the transformation plan to upgrade your project. Please try starting the transformation again." -export const errorDownloadingDiffMessage = - "Amazon Q couldn't download the diff with your upgraded code. Try downloading it again. For more information, see the [Amazon Q documentation](LINK_HERE)." +export const failedToCompleteJobChatMessage = + "Sorry, I couldn't complete the transformation. Please try starting the transformation again." -export const emptyDiffMessage = - "Amazon Q didn't make any changes to upgrade your code. Try restarting the transformation." +export const failedToCompleteJobNotification = + "Amazon Q couldn't complete the transformation. Please try starting the transformation again." -export const errorDeserializingDiffMessage = - "Amazon Q couldn't parse the diff with your upgraded code. Try restarting the transformation." +export const genericErrorMessage = + "Sorry, I'm experiencing technical issues at the moment. Please try again in a few minutes." -export const viewProposedChangesMessage = - 'Download complete. You can view a summary of the transformation and accept or reject the proposed changes in the Transformation Hub.' +export const jobCancelledChatMessage = + 'I cancelled your transformation. If you want to start another transformation, choose **Start a new transformation**.' -export const changesAppliedMessage = 'Amazon Q applied the changes to your project.' +export const jobCancelledNotification = 'You cancelled the transformation.' -export const noSupportedJavaProjectsFoundMessage = - 'None of your open projects are supported by Amazon Q Code Transformation. Currently, Amazon Q can only upgrade Java projects built on Maven. For more information, see the [Amazon Q documentation](LINK_HERE).' +export const jobCompletedChatMessage = + 'I upgraded your code to Java 17. You can review the diff to see my proposed changes and accept or reject them. The transformation summary has details about the files I updated.' -export const linkToDocsHome = 'https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/code-transformation.html' +export const jobCompletedNotification = + 'Amazon Q upgraded your code to Java 17. You can review the diff to see my proposed changes and accept or reject them. The transformation summary has details about the files I updated.' -export const linkToPrerequisites = - 'https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/code-transformation.html#prerequisites' +export const jobPartiallyCompletedChatMessage = + 'I upgraded part of your code to Java 17. You can review the diff to see my proposed changes and accept or reject them. The transformation summary has details about the files I updated and the errors that prevented a complete transformation.' -export const linkToMavenTroubleshooting = - 'https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html#maven-commands-failing' +export const jobPartiallyCompletedNotification = + 'Amazon Q upgraded part of your code to Java 17. You can review the diff to see my proposed changes and accept or reject them. The transformation summary has details about the files I updated and the errors that prevented a complete transformation.' -export const linkToUploadZipTooLarge = - 'https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html#project-size-limit' +export const noPomXmlFoundChatMessage = + "Sorry, I couldn't find a project that I can upgrade. I couldn't find a pom.xml file in any of your open projects. Currently, I can only upgrade Java 8 or Java 11 projects built on Maven. For more information, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/code-transformation.html#prerequisites)." -export const linkToDownloadZipTooLarge = - 'https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html#output-artifact-size-limit' +export const noPomXmlFoundNotification = + "None of your open projects are supported by Amazon Q Code Transformation. Amazon Q couldn't find a pom.xml file in any of your open projects. Currently, Amazon Q can only upgrade Java 8 or Java 11 projects built on Maven. For more information, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/code-transformation.html#prerequisites)." -export const dependencyDisclaimer = - 'Please confirm you are ready to proceed with the transformation. Amazon Q Code Transformation will upload the application code and its dependency binaries from your machine to start the upgrade. If you have not yet compiled the application on your local machine, please do so once before starting the upgrade.' +export const noJavaHomeFoundChatMessage = + "Sorry, I couldn't locate your Java installation. For more information, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/code-transformation.html#prerequisites)." -export const dependencyFolderName = 'transformation_dependencies_temp_' +export const errorStoppingJobChatMessage = "Sorry, I couldn't stop the transformation." -export const installErrorMessage = - "Amazon Q couldn't execute the Maven clean install command. To troubleshoot, see the [Amazon Q Code Transformation documentation](LINK_HERE)." +export const errorStoppingJobNotification = "Amazon Q couldn't stop the transformation." -export const dependencyErrorMessage = - "Amazon Q couldn't execute the Maven copy-dependencies command. To troubleshoot, see the [Amazon Q Code Transformation documentation](LINK_HERE)." +export const errorDownloadingDiffChatMessage = + "Sorry, I couldn't download the diff with your upgraded code. Please try downloading it again. For more information, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html#output-artifact-size-limit)." -export const planIntroductionMessage = - 'We reviewed your Java JAVA_VERSION_HERE application and generated a transformation plan. Any code changes made to your application will be done in the sandbox so as to not interfere with your working repository. Once the transformation job is done, we will share the new code which you can review before acccepting the code changes. In the meantime, you can work on your codebase and invoke Q Chat to answer questions about your codebase.' +export const errorDownloadingDiffNotification = + "Amazon Q couldn't download the diff with your upgraded code. Please try downloading it again. For more information, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html#output-artifact-size-limit)." -export const planDisclaimerMessage = '**Proposed transformation changes** \n\n\n' +export const errorDeserializingDiffChatMessage = + "Sorry, I couldn't parse the diff with your upgraded code. Please try starting the transformation again. For more information, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html#output-artifact-size-limit)." -export const numMillisecondsPerSecond = 1000 +export const errorDeserializingDiffNotification = + "Amazon Q couldn't parse the diff with your upgraded code. Please try starting the transformation again. For more information, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html#output-artifact-size-limit)." -export const uploadZipSizeLimitInBytes = 1000000000 // 1GB +export const viewProposedChangesChatMessage = + 'Download complete. You can view a summary of the transformation and accept or reject the proposed changes in the Transformation Hub.' -export const maxBufferSize = 1024 * 1024 * 8 // this is 8MB; the default max buffer size for stdout for spawnSync is 1MB +export const viewProposedChangesNotification = + 'Download complete. You can view a summary of the transformation and accept or reject the proposed changes in the Transformation Hub.' -export const transformByQStateRunningMessage = 'running' +export const changesAppliedChatMessage = 'I applied the changes to your project.' -export const transformByQStateCancellingMessage = 'cancelling' +export const changesAppliedNotification = 'Amazon Q applied the changes to your project.' -export const transformByQStateFailedMessage = 'failed' +export const noOpenProjectsFoundChatMessage = + "Sorry, I couldn't find a project that I can upgrade. Currently, I can only upgrade Java 8 or Java 11 projects built on Maven. For more information, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/code-transformation.html#prerequisites)." -export const transformByQStateSucceededMessage = 'succeeded' +export const noJavaProjectsFoundChatMessage = + "Sorry, I couldn't find a project that I can upgrade. Currently, I can only upgrade Java 8 or Java 11 projects built on Maven. For more information, see the [Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/code-transformation.html#prerequisites)." -export const transformByQStatePartialSuccessMessage = 'partially succeeded' +export const linkToDocsHome = 'https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/code-transformation.html' -export const transformByQStoppedState = 'STOPPED' +export const linkToPrerequisites = '' -export const transformationJobPollingIntervalSeconds = 5 +export const linkToMavenTroubleshooting = '' -export const transformationJobTimeoutSeconds = 60 * 60 // 1 hour, to match backend +export const linkToUploadZipTooLarge = + 'https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html#project-size-limit' -export const defaultLanguage = 'Java' +export const linkToDownloadZipTooLarge = '' -export const contentChecksumType = 'SHA_256' +export const dependencyFolderName = 'transformation_dependencies_temp_' -export const uploadIntent = 'TRANSFORMATION' +export const cleanInstallErrorChatMessage = + "Sorry, I couldn't run the Maven clean install command to build your project. For more information, see the [Amazon Q Code Transformation documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html#maven-commands-failing)." -export const transformationType = 'LANGUAGE_UPGRADE' +export const cleanInstallErrorNotification = + "Amazon Q couldn't run the Maven clean install command to build your project. For more information, see the [Amazon Q Code Transformation documentation](https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html#maven-commands-failing)." -// job successfully started -export const validStatesForJobStarted = [ - 'STARTED', - 'PREPARING', - 'PREPARED', - 'PLANNING', - 'PLANNED', - 'TRANSFORMING', - 'TRANSFORMED', -] +export const enterJavaHomeChatMessage = 'Enter the path to JDK ' -// initial build succeeded -export const validStatesForBuildSucceeded = ['PREPARED', 'PLANNING', 'PLANNED', 'TRANSFORMING', 'TRANSFORMED'] +export const projectPromptChatMessage = + 'I can upgrade your JAVA_VERSION_HERE. To start the transformation, I need some information from you. Choose the project you want to upgrade and the target code version to upgrade to. Then, choose Transform.' -// plan must be available -export const validStatesForPlanGenerated = ['PLANNED', 'TRANSFORMING', 'TRANSFORMED'] +export const windowsJavaHomeHelpChatMessage = + 'To find the JDK path, run the following command in a new IDE terminal: `cd "C:\\Program Files\\Java" && dir`. If you see your JDK version, run `cd ` and then `cd` to show the path.' -export const failureStates = ['FAILED', 'STOPPING', 'STOPPED', 'REJECTED'] +export const nonWindowsJava8HomeHelpChatMessage = + 'To find the JDK path, run the following command in a new IDE terminal: `/usr/libexec/java_home -v 1.8`' -// if status is COMPLETED or PARTIALLY_COMPLETED we can download artifacts -export const validStatesForCheckingDownloadUrl = [ - 'COMPLETED', - 'PARTIALLY_COMPLETED', - 'FAILED', - 'STOPPING', - 'STOPPED', - 'REJECTED', -] +export const nonWindowsJava11HomeHelpChatMessage = + 'To find the JDK path, run the following command in a new IDE terminal: `/usr/libexec/java_home -v 11`' + +export const projectSizeTooLargeChatMessage = + 'Sorry, your project size exceeds the Amazon Q Code Transformation upload limit of 1GB. For more information, see the [Code Transformation documentation](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/troubleshooting-code-transformation.html#project-size-limit).' + +export const projectSizeTooLargeNotification = + 'Your project size exceeds the Amazon Q Code Transformation upload limit of 1GB. For more information, see the [Code Transformation documentation](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/troubleshooting-code-transformation.html#project-size-limit).' + +export const JDK8VersionNumber = '52' + +export const JDK11VersionNumber = '55' + +export const planIntroductionMessage = + 'We reviewed your Java JAVA_VERSION_HERE application and generated a transformation plan. Any code changes made to your application will be done in the sandbox so as to not interfere with your working repository. Once the transformation job is done, we will share the new code which you can review before acccepting the code changes. In the meantime, you can work on your codebase and invoke Q Chat to answer questions about your codebase.' + +export const planDisclaimerMessage = '**Proposed transformation changes**\n\n' + +// end of QCT Strings export enum UserGroup { Classifier = 'Classifier', diff --git a/packages/core/src/codewhisperer/models/model.ts b/packages/core/src/codewhisperer/models/model.ts index 968b05bf687..c5db805f593 100644 --- a/packages/core/src/codewhisperer/models/model.ts +++ b/packages/core/src/codewhisperer/models/model.ts @@ -303,7 +303,9 @@ export class TransformByQState { private payloadFilePath: string = '' - private jobFailureErrorMessage: string = '' + private jobFailureErrorNotification: string | undefined = undefined + + private jobFailureErrorChatMessage: string | undefined = undefined private errorLog: string = '' @@ -393,8 +395,12 @@ export class TransformByQState { return this.payloadFilePath } - public getJobFailureErrorMessage() { - return this.jobFailureErrorMessage + public getJobFailureErrorNotification() { + return this.jobFailureErrorNotification + } + + public getJobFailureErrorChatMessage() { + return this.jobFailureErrorChatMessage } public getErrorLog() { @@ -497,8 +503,12 @@ export class TransformByQState { this.payloadFilePath = payloadFilePath } - public setJobFailureErrorMessage(errorMessage: string) { - this.jobFailureErrorMessage = errorMessage + public setJobFailureErrorNotification(errorNotification: string) { + this.jobFailureErrorNotification = errorNotification + } + + public setJobFailureErrorChatMessage(errorChatMessage: string) { + this.jobFailureErrorChatMessage = errorChatMessage } public setMavenName(mavenName: string) { @@ -544,7 +554,9 @@ export class TransformByQState { public setJobDefaults() { this.setToNotStarted() // so that the "Transform by Q" button resets this.polledJobStatus = '' // reset polled job status too - this.jobFailureErrorMessage = '' + this.jobFailureErrorNotification = undefined + this.jobFailureErrorChatMessage = undefined + this.jobFailureMetadata = '' this.payloadFilePath = '' this.errorLog = '' } diff --git a/packages/core/src/codewhisperer/service/transformByQ/transformApiHandler.ts b/packages/core/src/codewhisperer/service/transformByQ/transformApiHandler.ts index 3df572b1ce4..7ada4a98058 100644 --- a/packages/core/src/codewhisperer/service/transformByQ/transformApiHandler.ts +++ b/packages/core/src/codewhisperer/service/transformByQ/transformApiHandler.ts @@ -27,10 +27,10 @@ import { codeTransformTelemetryState } from '../../../amazonqGumby/telemetry/cod import { calculateTotalLatency } from '../../../amazonqGumby/telemetry/codeTransformTelemetry' import { MetadataResult } from '../../../shared/telemetry/telemetryClient' import request from '../../../common/request' -import { projectSizeTooLargeMessage } from '../../../amazonqGumby/chat/controller/messenger/stringConstants' import { ZipExceedsSizeLimitError } from '../../../amazonqGumby/errors' import { writeLogs } from './transformFileHandler' import { AuthUtil } from '../../util/authUtil' +import { ChatSessionManager } from '../../../amazonqGumby/chat/storages/chatSession' export function getSha256(buffer: Buffer) { const hasher = crypto.createHash('sha256') @@ -195,6 +195,27 @@ export async function uploadPayload(payloadFileName: string) { return response.uploadId } +/** + * Array of file extensions used by Maven as metadata in the local repository. + * Files with these extensions influence Maven's behavior during compile time, + * particularly in checking the availability of source repositories and potentially + * re-downloading dependencies if the source is not accessible. Removing these + * files can prevent Maven from attempting to download dependencies again. + */ +const MavenExcludedExtensions = ['.repositories', '.sha1'] + +/** + * Determines if the specified file path corresponds to a Maven metadata file + * by checking against known metadata file extensions. This is used to identify + * files that might trigger Maven to recheck or redownload dependencies from source repositories. + * + * @param path The file path to evaluate for exclusion based on its extension. + * @returns {boolean} Returns true if the path ends with an extension associated with Maven metadata files; otherwise, false. + */ +function isExcludedDependencyFile(path: string): boolean { + return MavenExcludedExtensions.some(extension => path.endsWith(extension)) +} + /** * Gets all files in dir. We use this method to get the source code, then we run a mvn command to * copy over dependencies into their own folder, then we use this method again to get those @@ -252,6 +273,9 @@ export async function zipCode(dependenciesFolder: FolderInfo) { if (dependencyFiles.length > 0) { for (const file of dependencyFiles) { + if (isExcludedDependencyFile(file)) { + continue + } const relativePath = path.relative(dependenciesFolder.path, file) const paddedPath = path.join(`dependencies/${dependenciesFolder.name}`, relativePath) zip.addLocalFile(file, path.dirname(paddedPath)) @@ -302,9 +326,11 @@ export async function zipCode(dependenciesFolder: FolderInfo) { }) if (exceedsLimit) { - void vscode.window.showErrorMessage( - projectSizeTooLargeMessage.replace('LINK_HERE', CodeWhispererConstants.linkToUploadZipTooLarge) - ) + void vscode.window.showErrorMessage(CodeWhispererConstants.projectSizeTooLargeNotification) + transformByQState.getChatControllers()?.transformationFinished.fire({ + message: CodeWhispererConstants.projectSizeTooLargeChatMessage, + tabID: ChatSessionManager.Instance.getSession().tabID, + }) throw new ZipExceedsSizeLimitError() } @@ -350,7 +376,7 @@ export async function startJob(uploadId: string) { result: MetadataResult.Fail, reason: 'StartTransformationFailed', }) - throw new Error('Start job failed') + throw new Error(`Start job failed: ${errorMessage}`) } } @@ -481,6 +507,13 @@ export async function pollTransformationJob(jobId: string, validStates: string[] } transformByQState.setPolledJobStatus(status) await vscode.commands.executeCommand('_aws.toolkit.amazonq.refreshTreeNode') + await vscode.commands.executeCommand('aws.amazonq.refresh') + const errorMessage = response.transformationJob.reason + if (errorMessage !== undefined) { + transformByQState.setJobFailureErrorChatMessage(errorMessage) + transformByQState.setJobFailureErrorNotification(errorMessage) + transformByQState.setJobFailureMetadata(` (request ID: ${response.$response.requestId})`) + } if (validStates.includes(status)) { break } @@ -489,9 +522,6 @@ export async function pollTransformationJob(jobId: string, validStates: string[] * is called, we break above on validStatesForCheckingDownloadUrl and check final status in finalizeTransformationJob */ if (CodeWhispererConstants.failureStates.includes(status)) { - transformByQState.setJobFailureMetadata( - `${response.transformationJob.reason} (request ID: ${response.$response.requestId})` - ) throw new Error('Job was rejected, stopped, or failed') } await sleep(CodeWhispererConstants.transformationJobPollingIntervalSeconds * 1000) diff --git a/packages/core/src/codewhisperer/service/transformByQ/transformMavenHandler.ts b/packages/core/src/codewhisperer/service/transformByQ/transformMavenHandler.ts index 6ed1044a5e6..12f1e07fd31 100644 --- a/packages/core/src/codewhisperer/service/transformByQ/transformMavenHandler.ts +++ b/packages/core/src/codewhisperer/service/transformByQ/transformMavenHandler.ts @@ -161,12 +161,7 @@ export async function prepareProjectDependencies(dependenciesFolder: FolderInfo) try { installProjectDependencies(dependenciesFolder) } catch (err) { - void vscode.window.showErrorMessage( - CodeWhispererConstants.installErrorMessage.replace( - 'LINK_HERE', - CodeWhispererConstants.linkToMavenTroubleshooting - ) - ) + void vscode.window.showErrorMessage(CodeWhispererConstants.cleanInstallErrorNotification) // open build-logs.txt file to show user error logs const logFilePath = await writeLogs() const doc = await vscode.workspace.openTextDocument(logFilePath) @@ -175,6 +170,7 @@ export async function prepareProjectDependencies(dependenciesFolder: FolderInfo) } throwIfCancelled() + void vscode.window.showInformationMessage(CodeWhispererConstants.buildSucceededNotification) } export async function getVersionData() { diff --git a/packages/core/src/codewhisperer/service/transformByQ/transformProjectValidationHandler.ts b/packages/core/src/codewhisperer/service/transformByQ/transformProjectValidationHandler.ts index d97359b4335..a241a6d1aa3 100644 --- a/packages/core/src/codewhisperer/service/transformByQ/transformProjectValidationHandler.ts +++ b/packages/core/src/codewhisperer/service/transformByQ/transformProjectValidationHandler.ts @@ -14,7 +14,6 @@ import { JDKToTelemetryValue, } from '../../../amazonqGumby/telemetry/codeTransformTelemetry' import { MetadataResult } from '../../../shared/telemetry/telemetryClient' -import { JDK11VersionNumber, JDK8VersionNumber } from '../../../amazonqGumby/chat/controller/messenger/stringConstants' import { NoJavaProjectsFoundError, NoMavenJavaProjectsFoundError, @@ -140,9 +139,9 @@ async function getProjectsValidToTransform( } else { const majorVersionIndex = spawnResult.stdout.indexOf('major version: ') const javaVersion = spawnResult.stdout.slice(majorVersionIndex + 15, majorVersionIndex + 17).trim() - if (javaVersion === JDK8VersionNumber) { + if (javaVersion === CodeWhispererConstants.JDK8VersionNumber) { detectedJavaVersion = JDKVersion.JDK8 - } else if (javaVersion === JDK11VersionNumber) { + } else if (javaVersion === CodeWhispererConstants.JDK11VersionNumber) { detectedJavaVersion = JDKVersion.JDK11 } else { detectedJavaVersion = JDKVersion.UNSUPPORTED @@ -179,7 +178,6 @@ export async function validateOpenProjects( if (javaProjects.length === 0) { if (!onProjectFirstOpen) { - void vscode.window.showErrorMessage(CodeWhispererConstants.noSupportedJavaProjectsFoundMessage) telemetry.codeTransform_isDoubleClickedToTriggerInvalidProject.emit({ codeTransformSessionId: codeTransformTelemetryState.getSessionId(), codeTransformPreValidationError: 'NoJavaProject', @@ -193,12 +191,7 @@ export async function validateOpenProjects( const mavenJavaProjects = await getMavenJavaProjects(javaProjects) if (mavenJavaProjects.length === 0) { if (!onProjectFirstOpen) { - void vscode.window.showErrorMessage( - CodeWhispererConstants.noPomXmlFoundMessage.replace( - 'LINK_HERE', - CodeWhispererConstants.linkToPrerequisites - ) - ) + void vscode.window.showErrorMessage(CodeWhispererConstants.noPomXmlFoundNotification) telemetry.codeTransform_isDoubleClickedToTriggerInvalidProject.emit({ codeTransformSessionId: codeTransformTelemetryState.getSessionId(), codeTransformPreValidationError: 'NonMavenProject', diff --git a/packages/core/src/codewhisperer/service/transformByQ/transformationResultsViewProvider.ts b/packages/core/src/codewhisperer/service/transformByQ/transformationResultsViewProvider.ts index 7de39ee8dfc..125687b814d 100644 --- a/packages/core/src/codewhisperer/service/transformByQ/transformationResultsViewProvider.ts +++ b/packages/core/src/codewhisperer/service/transformByQ/transformationResultsViewProvider.ts @@ -19,6 +19,7 @@ import { calculateTotalLatency } from '../../../amazonqGumby/telemetry/codeTrans import { MetadataResult } from '../../../shared/telemetry/telemetryClient' import * as CodeWhispererConstants from '../../models/constants' import { createCodeWhispererChatStreamingClient } from '../../../shared/clients/codewhispererChatClient' +import { ChatSessionManager } from '../../../amazonqGumby/chat/storages/chatSession' export abstract class ProposedChangeNode { abstract readonly resourcePath: string @@ -138,10 +139,6 @@ export class DiffModel { */ public parseDiff(pathToDiff: string, pathToWorkspace: string): ProposedChangeNode[] { const diffContents = fs.readFileSync(pathToDiff, 'utf8') - if (!diffContents) { - void vscode.window.showErrorMessage(CodeWhispererConstants.emptyDiffMessage) - throw new Error('diff.patch file is empty') - } const changedFiles = parsePatch(diffContents) // path to the directory containing copy of the changed files in the transformed project const pathToTmpSrcDir = this.copyProject(pathToWorkspace, changedFiles) @@ -315,12 +312,11 @@ export class ProposedTransformationExplorer { } catch (e: any) { downloadErrorMessage = (e as Error).message // This allows the customer to retry the download - void vscode.window.showErrorMessage( - CodeWhispererConstants.errorDownloadingDiffMessage.replace( - 'LINK_HERE', - CodeWhispererConstants.linkToDownloadZipTooLarge - ) - ) + void vscode.window.showErrorMessage(CodeWhispererConstants.errorDownloadingDiffNotification) + transformByQState.getChatControllers()?.transformationFinished.fire({ + message: CodeWhispererConstants.errorDownloadingDiffChatMessage, + tabID: ChatSessionManager.Instance.getSession().tabID, + }) await vscode.commands.executeCommand( 'setContext', 'gumby.reviewState', @@ -382,12 +378,20 @@ export class ProposedTransformationExplorer { }) // Do not await this so that the summary reveals without user needing to close this notification - void vscode.window.showInformationMessage(CodeWhispererConstants.viewProposedChangesMessage) + void vscode.window.showInformationMessage(CodeWhispererConstants.viewProposedChangesNotification) + transformByQState.getChatControllers()?.transformationFinished.fire({ + message: CodeWhispererConstants.viewProposedChangesChatMessage, + tabID: ChatSessionManager.Instance.getSession().tabID, + }) await vscode.commands.executeCommand('aws.amazonq.transformationHub.summary.reveal') } catch (e: any) { deserializeErrorMessage = (e as Error).message getLogger().error(`CodeTransformation: ParseDiff error = ${deserializeErrorMessage}`) - void vscode.window.showErrorMessage(CodeWhispererConstants.errorDeserializingDiffMessage) + transformByQState.getChatControllers()?.transformationFinished.fire({ + message: CodeWhispererConstants.errorDeserializingDiffChatMessage, + tabID: ChatSessionManager.Instance.getSession().tabID, + }) + void vscode.window.showErrorMessage(CodeWhispererConstants.errorDeserializingDiffNotification) } finally { telemetry.codeTransform_jobArtifactDownloadAndDeserializeTime.emit({ codeTransformSessionId: codeTransformTelemetryState.getSessionId(), @@ -407,7 +411,11 @@ export class ProposedTransformationExplorer { await vscode.commands.executeCommand('setContext', 'gumby.transformationProposalReviewInProgress', false) await vscode.commands.executeCommand('setContext', 'gumby.reviewState', TransformByQReviewStatus.NotStarted) transformDataProvider.refresh() - await vscode.window.showInformationMessage(CodeWhispererConstants.changesAppliedMessage) + await vscode.window.showInformationMessage(CodeWhispererConstants.changesAppliedNotification) + transformByQState.getChatControllers()?.transformationFinished.fire({ + message: CodeWhispererConstants.changesAppliedChatMessage, + tabID: ChatSessionManager.Instance.getSession().tabID, + }) // delete result archive and copied source code after changes accepted fs.rmSync(transformByQState.getResultArchiveFilePath(), { recursive: true, force: true }) fs.rmSync(transformByQState.getProjectCopyFilePath(), { recursive: true, force: true }) diff --git a/packages/core/src/codewhisperer/util/authUtil.ts b/packages/core/src/codewhisperer/util/authUtil.ts index 102920347f0..bf5ffadcbf8 100644 --- a/packages/core/src/codewhisperer/util/authUtil.ts +++ b/packages/core/src/codewhisperer/util/authUtil.ts @@ -61,20 +61,15 @@ export const isValidCodeWhispererCoreConnection = (conn?: Connection): conn is C (isSsoConnection(conn) && hasScopes(conn, codeWhispererCoreScopes)) ) } -/** For Builder ID only, if using IdC then use {@link isValidAmazonQConnection} */ -export const isValidCodeWhispererChatConnection = (conn?: Connection): conn is Connection => { +/** Superset that includes all of CodeWhisperer + Amazon Q */ +export const isValidAmazonQConnection = (conn?: Connection): conn is Connection => { return ( - isBuilderIdConnection(conn) && + (isSsoConnection(conn) || isBuilderIdConnection(conn)) && isValidCodeWhispererCoreConnection(conn) && - hasScopes(conn, codeWhispererChatScopes) + hasScopes(conn, amazonQScopes) ) } -/** Superset that includes all of CodeWhisperer + Amazon Q */ -export const isValidAmazonQConnection = (conn?: Connection): conn is Connection => { - return isSsoConnection(conn) && isValidCodeWhispererCoreConnection(conn) && hasScopes(conn, amazonQScopes) -} - interface HasAlreadySeenQWelcome { local?: boolean devEnv?: boolean @@ -247,9 +242,9 @@ export class AuthUtil { let conn = (await this.auth.listConnections()).find(isBuilderIdConnection) if (!conn) { - conn = await this.auth.createConnection(createBuilderIdProfile(codeWhispererChatScopes)) - } else if (!isValidCodeWhispererChatConnection(conn)) { - conn = await this.secondaryAuth.addScopes(conn, codeWhispererChatScopes) + conn = await this.auth.createConnection(createBuilderIdProfile(amazonQScopes)) + } else if (!isValidAmazonQConnection(conn)) { + conn = await this.secondaryAuth.addScopes(conn, amazonQScopes) } if (this.auth.getConnectionState(conn) === 'invalid') { @@ -363,11 +358,10 @@ export class AuthUtil { // Edge Case: With the addition of Amazon Q/Chat scopes we may need to add // the new scopes to existing pre-chat connections. if (addMissingScopes) { - if (isBuilderIdConnection(this.conn) && !isValidCodeWhispererChatConnection(this.conn)) { - const conn = await this.secondaryAuth.addScopes(this.conn, codeWhispererChatScopes) - await this.secondaryAuth.useNewConnection(conn) - return - } else if (isIdcSsoConnection(this.conn) && !isValidAmazonQConnection(this.conn)) { + if ( + (isBuilderIdConnection(this.conn) || isIdcSsoConnection(this.conn)) && + !isValidAmazonQConnection(this.conn) + ) { const conn = await this.secondaryAuth.addScopes(this.conn, amazonQScopes) await this.secondaryAuth.useNewConnection(conn) return @@ -413,7 +407,7 @@ export class AuthUtil { } public isValidCodeTransformationAuthUser(): boolean { - return this.isEnterpriseSsoInUse() && this.isConnectionValid() + return (this.isEnterpriseSsoInUse() || this.isBuilderIdInUse()) && this.isConnectionValid() } /** @@ -444,23 +438,11 @@ export class AuthUtil { // default to expired to indicate reauth is needed if unmodified const state: FeatureAuthState = buildFeatureAuthState(AuthStates.expired) - if (isBuilderIdConnection(currentConnection)) { - // Regardless, if using Builder ID, Amazon Q is unsupported - state[Features.amazonQ] = AuthStates.unsupported - } - if (this.isConnectionExpired()) { return state } - if (isBuilderIdConnection(currentConnection)) { - if (isValidCodeWhispererCoreConnection(currentConnection)) { - state[Features.codewhispererCore] = AuthStates.connected - } - if (isValidCodeWhispererChatConnection(currentConnection)) { - state[Features.codewhispererChat] = AuthStates.connected - } - } else if (isIdcSsoConnection(currentConnection)) { + if (isBuilderIdConnection(currentConnection) || isIdcSsoConnection(currentConnection)) { if (isValidCodeWhispererCoreConnection(currentConnection)) { state[Features.codewhispererCore] = AuthStates.connected } diff --git a/packages/core/src/test/amazonqFeatureDev/session/chatSessionStorage.test.ts b/packages/core/src/test/amazonqFeatureDev/session/chatSessionStorage.test.ts new file mode 100644 index 00000000000..4248b6ce745 --- /dev/null +++ b/packages/core/src/test/amazonqFeatureDev/session/chatSessionStorage.test.ts @@ -0,0 +1,26 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as assert from 'assert' + +import { Messenger } from '../../../amazonqFeatureDev/controllers/chat/messenger/messenger' +import { ChatSessionStorage } from '../../../amazonqFeatureDev/storages/chatSession' +import { createMessenger } from '../utils' + +describe('chatSession', () => { + const tabID = '1234' + let chatStorage: ChatSessionStorage + let messenger: Messenger + + beforeEach(() => { + messenger = createMessenger() + chatStorage = new ChatSessionStorage(messenger) + }) + + it('locks getSession', async () => { + const results = await Promise.allSettled([chatStorage.getSession(tabID), chatStorage.getSession(tabID)]) + assert.equal(results.length, 2) + assert.deepStrictEqual(results[0], results[1]) + }) +}) diff --git a/packages/core/src/test/codewhisperer/commands/transformByQ.test.ts b/packages/core/src/test/codewhisperer/commands/transformByQ.test.ts index 87bb1483c7b..236689f15a2 100644 --- a/packages/core/src/test/codewhisperer/commands/transformByQ.test.ts +++ b/packages/core/src/test/codewhisperer/commands/transformByQ.test.ts @@ -5,16 +5,17 @@ import assert from 'assert' import * as vscode from 'vscode' +import * as fs from 'fs-extra' import * as sinon from 'sinon' +import { makeTemporaryToolkitFolder } from '../../../shared/filesystemUtilities' import * as model from '../../../codewhisperer/models/model' import * as startTransformByQ from '../../../codewhisperer/commands/startTransformByQ' import { HttpResponse } from 'aws-sdk' import * as codeWhisperer from '../../../codewhisperer/client/codewhisperer' import * as CodeWhispererConstants from '../../../codewhisperer/models/constants' -import { getTestWindow } from '../../shared/vscode/window' -import { stopTransformByQMessage } from '../../../codewhisperer/models/constants' import { convertToTimeString, convertDateToTimestamp } from '../../../shared/utilities/textUtilities' import path from 'path' +import AdmZip from 'adm-zip' import { createTestWorkspaceFolder, toFile } from '../../testUtil' import { NoJavaProjectsFoundError, @@ -26,6 +27,7 @@ import { pollTransformationJob, getHeadersObj, throwIfCancelled, + zipCode, } from '../../../codewhisperer/service/transformByQ/transformApiHandler' import { validateOpenProjects, @@ -34,8 +36,15 @@ import { import { TransformationCandidateProject } from '../../../codewhisperer/models/model' describe('transformByQ', function () { - afterEach(function () { + let tempDir: string + + beforeEach(async function () { + tempDir = await makeTemporaryToolkitFolder() + }) + + afterEach(async function () { sinon.restore() + await fs.remove(tempDir) }) it('WHEN converting short duration in milliseconds THEN converts correctly', async function () { @@ -67,12 +76,6 @@ describe('transformByQ', function () { }) it('WHEN job is stopped THEN status is updated to cancelled', async function () { - const testWindow = getTestWindow() - testWindow.onDidShowMessage(message => { - if (message.message === stopTransformByQMessage) { - message.selectItem(startTransformByQ.stopTransformByQButton) - } - }) model.transformByQState.setToRunning() await startTransformByQ.stopTransformByQ('abc-123') assert.strictEqual(model.transformByQState.getStatus(), 'Cancelled') @@ -199,4 +202,54 @@ describe('transformByQ', function () { } assert.deepStrictEqual(actual, expected) }) + + it(`WHEN zip created THEN dependencies contains no .sha1 or .repositories files`, async function () { + const m2Folders = [ + 'com/groupid1/artifactid1/version1', + 'com/groupid1/artifactid1/version2', + 'com/groupid1/artifactid2/version1', + 'com/groupid2/artifactid1/version1', + 'com/groupid2/artifactid1/version2', + ] + // List of files that exist in m2 artifact directory + const filesToAdd = [ + '_remote.repositories', + 'test-0.0.1-20240315.145420-18.pom', + 'test-0.0.1-20240315.145420-18.pom.sha1', + 'test-0.0.1-SNAPSHOT.pom', + 'maven-metadata-test-repo.xml', + 'maven-metadata-test-repo.xml.sha1', + 'resolver-status.properties', + ] + const expectedFilesAfterClean = [ + 'test-0.0.1-20240315.145420-18.pom', + 'test-0.0.1-SNAPSHOT.pom', + 'maven-metadata-test-repo.xml', + 'resolver-status.properties', + ] + + m2Folders.forEach(folder => { + const folderPath = path.join(tempDir, folder) + fs.mkdirSync(folderPath, { recursive: true }) + filesToAdd.forEach(file => { + fs.writeFileSync(path.join(folderPath, file), 'sample content for the test file') + }) + }) + + const tempFileName = `testfile-${Date.now()}.zip` + model.transformByQState.setProjectPath(tempDir) + return zipCode({ + path: tempDir, + name: tempFileName, + }).then(zipFile => { + const zip = new AdmZip(zipFile) + const dependenciesToUpload = zip.getEntries().filter(entry => entry.entryName.startsWith('dependencies')) + // Each dependency version folder contains each expected file, thus we multiply + const expectedNumberOfDependencyFiles = m2Folders.length * expectedFilesAfterClean.length + assert.strictEqual(expectedNumberOfDependencyFiles, dependenciesToUpload.length) + dependenciesToUpload.forEach(dependency => { + assert(expectedFilesAfterClean.includes(dependency.name)) + }) + }) + }) }) diff --git a/packages/core/src/test/codewhisperer/util/authUtil.test.ts b/packages/core/src/test/codewhisperer/util/authUtil.test.ts index a4454ecd428..57f9173be3b 100644 --- a/packages/core/src/test/codewhisperer/util/authUtil.test.ts +++ b/packages/core/src/test/codewhisperer/util/authUtil.test.ts @@ -43,7 +43,7 @@ describe('AuthUtil', async function () { const conn = authUtil.conn assert.strictEqual(conn?.type, 'sso') assert.strictEqual(conn.label, 'AWS Builder ID') - assert.deepStrictEqual(conn.scopes, codeWhispererChatScopes) + assert.deepStrictEqual(conn.scopes, amazonQScopes) }) it('if there IS an existing AwsBuilderID conn, it will upgrade the scopes and use it', async function () { @@ -60,7 +60,7 @@ describe('AuthUtil', async function () { const conn = authUtil.conn assert.strictEqual(conn?.type, 'sso') assert.strictEqual(conn.id, existingBuilderId.id) - assert.deepStrictEqual(conn.scopes, codeWhispererChatScopes) + assert.deepStrictEqual(conn.scopes, amazonQScopes) }) it('if there is no valid enterprise SSO conn, will create and use one', async function () { @@ -151,7 +151,7 @@ describe('AuthUtil', async function () { assert.deepStrictEqual(authUtil.conn?.scopes, codeWhispererCoreScopes) }) - it('reauthenticate adds missing CodeWhisperer Chat Builder ID scopes when explicitly required', async function () { + it('reauthenticate adds missing Builder ID scopes when explicitly required', async function () { const conn = await auth.createConnection(createBuilderIdProfile({ scopes: codeWhispererCoreScopes })) await auth.useConnection(conn) @@ -159,7 +159,7 @@ describe('AuthUtil', async function () { await authUtil.reauthenticate(true) assert.strictEqual(authUtil.conn?.type, 'sso') - assert.deepStrictEqual(authUtil.conn?.scopes, codeWhispererChatScopes) + assert.deepStrictEqual(authUtil.conn?.scopes, amazonQScopes) }) it('reauthenticate adds missing Amazon Q IdC scopes when explicitly required', async function () { @@ -218,7 +218,7 @@ describe('AuthUtil', async function () { assert.strictEqual(authUtil.conn?.id, upgradeableConn.id) assert.strictEqual(authUtil.conn.startUrl, upgradeableConn.startUrl) assert.strictEqual(authUtil.conn.ssoRegion, upgradeableConn.ssoRegion) - assert.deepStrictEqual(authUtil.conn.scopes, codeWhispererChatScopes) + assert.deepStrictEqual(authUtil.conn.scopes, amazonQScopes) assert.strictEqual((await auth.listConnections()).filter(isAnySsoConnection).length, 1) }) @@ -273,12 +273,12 @@ describe('getChatAuthState()', function () { assert.deepStrictEqual(result, { codewhispererCore: AuthStates.connected, codewhispererChat: AuthStates.expired, - amazonQ: AuthStates.unsupported, + amazonQ: AuthStates.expired, }) }) it('indicates all SUPPORTED features connected when all scopes are set', async function () { - const conn = await auth.createConnection(createBuilderIdProfile({ scopes: codeWhispererChatScopes })) + const conn = await auth.createConnection(createBuilderIdProfile({ scopes: amazonQScopes })) createToken(conn) await auth.useConnection(conn) @@ -286,7 +286,7 @@ describe('getChatAuthState()', function () { assert.deepStrictEqual(result, { codewhispererCore: AuthStates.connected, codewhispererChat: AuthStates.connected, - amazonQ: AuthStates.unsupported, + amazonQ: AuthStates.connected, }) }) @@ -300,7 +300,7 @@ describe('getChatAuthState()', function () { assert.deepStrictEqual(result, { codewhispererCore: AuthStates.expired, codewhispererChat: AuthStates.expired, - amazonQ: AuthStates.unsupported, + amazonQ: AuthStates.expired, }) }) }) diff --git a/packages/core/src/testE2E/util/codewhispererUtil.ts b/packages/core/src/testE2E/util/codewhispererUtil.ts index 4efe258c0b6..2ae7b8a34b8 100644 --- a/packages/core/src/testE2E/util/codewhispererUtil.ts +++ b/packages/core/src/testE2E/util/codewhispererUtil.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { isValidCodeWhispererChatConnection } from '../../codewhisperer/util/authUtil' +import { isValidAmazonQConnection } from '../../codewhisperer/util/authUtil' import { Auth } from '../../auth/auth' /* @@ -17,7 +17,7 @@ If user has an expired connection they must reauthenticate prior to running test */ async function getValidConnection() { - return (await Auth.instance.listConnections()).find(isValidCodeWhispererChatConnection) + return (await Auth.instance.listConnections()).find(isValidAmazonQConnection) } //Returns true if a valid connection is found and set, false if not diff --git a/packages/toolkit/.changes/next-release/Bug Fix-1be65539-66a5-4443-ae5e-2a93a67fe436.json b/packages/toolkit/.changes/next-release/Bug Fix-1be65539-66a5-4443-ae5e-2a93a67fe436.json new file mode 100644 index 00000000000..24bb3fe80fa --- /dev/null +++ b/packages/toolkit/.changes/next-release/Bug Fix-1be65539-66a5-4443-ae5e-2a93a67fe436.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "Amazon Q CodeTransform may fail if JAVA_HOME has leading or trailing whitespace" +} diff --git a/packages/toolkit/.changes/next-release/Bug Fix-5d6c3406-8213-4aec-ba80-a488e9728e3a.json b/packages/toolkit/.changes/next-release/Bug Fix-5d6c3406-8213-4aec-ba80-a488e9728e3a.json new file mode 100644 index 00000000000..4aadbd1636a --- /dev/null +++ b/packages/toolkit/.changes/next-release/Bug Fix-5d6c3406-8213-4aec-ba80-a488e9728e3a.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "Amazon Q Code Transformation: show error messages in chat" +} diff --git a/packages/toolkit/.changes/next-release/Bug Fix-ffcf9c8e-bd5e-4b34-a418-469717d913f6.json b/packages/toolkit/.changes/next-release/Bug Fix-ffcf9c8e-bd5e-4b34-a418-469717d913f6.json new file mode 100644 index 00000000000..f5b19414cdd --- /dev/null +++ b/packages/toolkit/.changes/next-release/Bug Fix-ffcf9c8e-bd5e-4b34-a418-469717d913f6.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "Amazon Q Code Transformation - Omit Maven metadata files when uploading dependencies to fix certain build failures in backend." +} diff --git a/packages/toolkit/.changes/next-release/Feature-51070f02-21b0-447b-9743-e7ce616337fe.json b/packages/toolkit/.changes/next-release/Feature-51070f02-21b0-447b-9743-e7ce616337fe.json new file mode 100644 index 00000000000..3bab41b73c7 --- /dev/null +++ b/packages/toolkit/.changes/next-release/Feature-51070f02-21b0-447b-9743-e7ce616337fe.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "Enable Amazon Q feature development and Amazon Q transform capabilities (/dev and /transform) for AWS Builder ID users." +} diff --git a/packages/toolkit/.vscode/extensions.json b/packages/toolkit/.vscode/extensions.json deleted file mode 100644 index b1a2d99f09e..00000000000 --- a/packages/toolkit/.vscode/extensions.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": ["dbaeumer.vscode-eslint"] -}