diff --git a/packages/core/src/amazonqTest/chat/controller/controller.ts b/packages/core/src/amazonqTest/chat/controller/controller.ts index 35e234cc01a..24c3f50af85 100644 --- a/packages/core/src/amazonqTest/chat/controller/controller.ts +++ b/packages/core/src/amazonqTest/chat/controller/controller.ts @@ -20,6 +20,7 @@ import { TelemetryHelper, TestGenerationBuildStep, testGenState, + tooManyRequestErrorMessage, unitTestGenerationCancelMessage, } from '../../../codewhisperer' import { @@ -241,71 +242,80 @@ export class TestController { // eslint-disable-next-line unicorn/no-null this.messenger.sendUpdatePromptProgress(data.tabID, null) const session = this.sessionStorage.getSession() - const isCancel = data.error.message === unitTestGenerationCancelMessage - + const isCancel = data.error.uiMessage === unitTestGenerationCancelMessage + let telemetryErrorMessage = getTelemetryReasonDesc(data.error) + if (session.stopIteration) { + telemetryErrorMessage = getTelemetryReasonDesc(data.error.uiMessage.replaceAll('```', '')) + } TelemetryHelper.instance.sendTestGenerationToolkitEvent( session, true, isCancel ? 'Cancelled' : 'Failed', session.startTestGenerationRequestId, performance.now() - session.testGenerationStartTime, - getTelemetryReasonDesc(data.error), + telemetryErrorMessage, session.isCodeBlockSelected, session.artifactsUploadDuration, session.srcPayloadSize, - session.srcZipFileSize + session.srcZipFileSize, + session.charsOfCodeAccepted, + session.numberOfTestsGenerated, + session.linesOfCodeAccepted, + session.charsOfCodeGenerated, + session.numberOfTestsGenerated, + session.linesOfCodeGenerated, + data.error.code ) if (session.stopIteration) { // Error from Science - this.messenger.sendMessage(data.error.message.replaceAll('```', ''), data.tabID, 'answer') + this.messenger.sendMessage(data.error.uiMessage.replaceAll('```', ''), data.tabID, 'answer') } else { isCancel - ? this.messenger.sendMessage(data.error.message, data.tabID, 'answer') + ? this.messenger.sendMessage(data.error.uiMessage, data.tabID, 'answer') : this.sendErrorMessage(data) } await this.sessionCleanUp() return } // Client side error messages - private sendErrorMessage(data: { tabID: string; error: { code: string; message: string } }) { + private sendErrorMessage(data: { tabID: string; error: { uiMessage: string; message: string; code: string } }) { const { error, tabID } = data + // If user reached monthly limit for builderId + if (error.code === 'CreateTestJobError') { + if (error.message.includes(CodeWhispererConstants.utgLimitReached)) { + getLogger().error('Monthly quota reached for QSDA actions.') + return this.messenger.sendMessage( + i18n('AWS.amazonq.featureDev.error.monthlyLimitReached'), + tabID, + 'answer' + ) + } + if (error.message.includes('Too many requests')) { + getLogger().error(error.message) + return this.messenger.sendErrorMessage(tooManyRequestErrorMessage, tabID) + } + } if (isAwsError(error)) { if (error.code === 'ThrottlingException') { - // TODO: use the explicitly modeled exception reason for quota vs throttle - if (error.message.includes(CodeWhispererConstants.utgLimitReached)) { - getLogger().error('Monthly quota reached for QSDA actions.') - return this.messenger.sendMessage( - i18n('AWS.amazonq.featureDev.error.monthlyLimitReached'), - tabID, - 'answer' - ) - } else { - getLogger().error('Too many requests.') - // TODO: move to constants file - this.messenger.sendErrorMessage('Too many requests. Please wait before retrying.', tabID) - } - } else { - // other service errors: - // AccessDeniedException - should not happen because access is validated before this point in the client - // ValidationException - shouldn't happen because client should not send malformed requests - // ConflictException - should not happen because the client will maintain proper state - // InternalServerException - shouldn't happen but needs to be caught - getLogger().error('Other error message: %s', error.message) - this.messenger.sendErrorMessage( - 'Encountered an unexpected error when generating tests. Please try again', - tabID - ) + // TODO: use the explicitly modeled exception reason for quota vs throttle{ + getLogger().error(error.message) + this.messenger.sendErrorMessage(tooManyRequestErrorMessage, tabID) + return } - } else { - // other unexpected errors (TODO enumerate all other failure cases) + // other service errors: + // AccessDeniedException - should not happen because access is validated before this point in the client + // ValidationException - shouldn't happen because client should not send malformed requests + // ConflictException - should not happen because the client will maintain proper state + // InternalServerException - shouldn't happen but needs to be caught getLogger().error('Other error message: %s', error.message) - this.messenger.sendErrorMessage( - 'Encountered an unexpected error when generating tests. Please try again', - tabID - ) + this.messenger.sendErrorMessage('', tabID) + return } + // other unexpected errors (TODO enumerate all other failure cases) + getLogger().error('Other error message: %s', error.uiMessage) + this.messenger.sendErrorMessage('', tabID) } // This function handles actions if user clicked on any Button one of these cases will be executed @@ -719,6 +729,9 @@ export class TestController { // this.messenger.sendMessage('Accepted', message.tabID, 'prompt') telemetry.ui_click.emit({ elementId: 'unitTestGeneration_acceptDiff' }) + getLogger().info( + `Generated unit tests are accepted for ${session.fileLanguage ?? 'plaintext'} language with jobId: ${session.listOfTestGenerationJobId[0]}, jobGroupName: ${session.testGenerationJobGroupName}, result: Succeeded` + ) TelemetryHelper.instance.sendTestGenerationToolkitEvent( session, true, @@ -739,7 +752,6 @@ export class TestController { ) await this.endSession(message, FollowUpTypes.SkipBuildAndFinish) - await this.sessionCleanUp() return if (session.listOfTestGenerationJobId.length === 1) { @@ -858,16 +870,12 @@ export class TestController { session.numberOfTestsGenerated, session.linesOfCodeGenerated ) - telemetry.ui_click.emit({ elementId: 'unitTestGeneration_rejectDiff' }) } await this.sessionCleanUp() - // TODO: revert 'Accepted' to 'Skip build and finish' once supported - const message = step === FollowUpTypes.RejectCode ? 'Rejected' : 'Accepted' - this.messenger.sendMessage(message, data.tabID, 'prompt') - this.messenger.sendMessage(`Unit test generation workflow is completed.`, data.tabID, 'answer') + // this.messenger.sendMessage(`Unit test generation workflow is completed.`, data.tabID, 'answer') this.messenger.sendChatInputEnabled(data.tabID, true) return } @@ -1302,8 +1310,18 @@ export class TestController { 'Deleting output.log and temp result directory. testGenerationLogsDir: %s', testGenerationLogsDir ) - await fs.delete(path.join(testGenerationLogsDir, 'output.log')) - await fs.delete(this.tempResultDirPath, { recursive: true }) + const outputLogPath = path.join(testGenerationLogsDir, 'output.log') + if (await fs.existsFile(outputLogPath)) { + await fs.delete(outputLogPath) + } + if ( + await fs + .stat(this.tempResultDirPath) + .then(() => true) + .catch(() => false) + ) { + await fs.delete(this.tempResultDirPath, { recursive: true }) + } } // TODO: return build command when product approves diff --git a/packages/core/src/amazonqTest/chat/controller/messenger/messenger.ts b/packages/core/src/amazonqTest/chat/controller/messenger/messenger.ts index 10b496b69d3..fc159eeabd2 100644 --- a/packages/core/src/amazonqTest/chat/controller/messenger/messenger.ts +++ b/packages/core/src/amazonqTest/chat/controller/messenger/messenger.ts @@ -280,9 +280,21 @@ export class Messenger { 'Cancelled', messageId, performance.now() - session.testGenerationStartTime, - getTelemetryReasonDesc(CodeWhispererConstants.unitTestGenerationCancelMessage) + getTelemetryReasonDesc( + `TestGenCancelled: ${CodeWhispererConstants.unitTestGenerationCancelMessage}` + ), + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + 'TestGenCancelled' ) - this.dispatcher.sendUpdatePromptProgress( new UpdatePromptProgressMessage(tabID, cancellingProgressField) ) @@ -293,9 +305,9 @@ export class Messenger { false, 'Succeeded', messageId, - performance.now() - session.testGenerationStartTime + performance.now() - session.testGenerationStartTime, + undefined ) - this.dispatcher.sendUpdatePromptProgress( new UpdatePromptProgressMessage(tabID, testGenCompletedField) ) diff --git a/packages/core/src/amazonqTest/error.ts b/packages/core/src/amazonqTest/error.ts new file mode 100644 index 00000000000..a6694b35863 --- /dev/null +++ b/packages/core/src/amazonqTest/error.ts @@ -0,0 +1,67 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { ToolkitError } from '../shared/errors' + +export const technicalErrorCustomerFacingMessage = + 'I am experiencing technical difficulties at the moment. Please try again in a few minutes.' +const defaultTestGenErrorMessage = 'Amazon Q encountered an error while generating tests. Try again later.' +export class TestGenError extends ToolkitError { + constructor( + error: string, + code: string, + public uiMessage: string + ) { + super(error, { code }) + } +} +export class ProjectZipError extends TestGenError { + constructor(error: string) { + super(error, 'ProjectZipError', defaultTestGenErrorMessage) + } +} +export class InvalidSourceZipError extends TestGenError { + constructor() { + super('Failed to create valid source zip', 'InvalidSourceZipError', defaultTestGenErrorMessage) + } +} +export class CreateUploadUrlError extends TestGenError { + constructor(errorMessage: string) { + super(errorMessage, 'CreateUploadUrlError', technicalErrorCustomerFacingMessage) + } +} +export class UploadTestArtifactToS3Error extends TestGenError { + constructor(error: string) { + super(error, 'UploadTestArtifactToS3Error', technicalErrorCustomerFacingMessage) + } +} +export class CreateTestJobError extends TestGenError { + constructor(error: string) { + super(error, 'CreateTestJobError', technicalErrorCustomerFacingMessage) + } +} +export class TestGenTimedOutError extends TestGenError { + constructor() { + super( + 'Test generation failed. Amazon Q timed out.', + 'TestGenTimedOutError', + technicalErrorCustomerFacingMessage + ) + } +} +export class TestGenStoppedError extends TestGenError { + constructor() { + super('Unit test generation cancelled.', 'TestGenCancelled', 'Unit test generation cancelled.') + } +} +export class TestGenFailedError extends TestGenError { + constructor(error?: string) { + super(error ?? 'Test generation failed', 'TestGenFailedError', error ?? technicalErrorCustomerFacingMessage) + } +} +export class ExportResultsArchiveError extends TestGenError { + constructor(error?: string) { + super(error ?? 'Test generation failed', 'ExportResultsArchiveError', technicalErrorCustomerFacingMessage) + } +} diff --git a/packages/core/src/codewhisperer/commands/startTestGeneration.ts b/packages/core/src/codewhisperer/commands/startTestGeneration.ts index 3b0f41ab1d6..c1e45e692ab 100644 --- a/packages/core/src/codewhisperer/commands/startTestGeneration.ts +++ b/packages/core/src/codewhisperer/commands/startTestGeneration.ts @@ -21,7 +21,7 @@ import { ChildProcess, spawn } from 'child_process' // eslint-disable-line no-re import { BuildStatus } from '../../amazonqTest/chat/session/session' import { fs } from '../../shared/fs/fs' import { TestGenerationJobStatus } from '../models/constants' -import { TestGenFailedError } from '../models/errors' +import { TestGenFailedError } from '../../amazonqTest/error' import { Range } from '../client/codewhispereruserclient' // eslint-disable-next-line unicorn/no-null @@ -75,8 +75,9 @@ export async function startTestGenerationProcess( try { artifactMap = await getPresignedUrlAndUploadTestGen(zipMetadata) } finally { - if (await fs.existsFile(path.join(testGenerationLogsDir, 'output.log'))) { - await fs.delete(path.join(testGenerationLogsDir, 'output.log')) + const outputLogPath = path.join(testGenerationLogsDir, 'output.log') + if (await fs.existsFile(outputLogPath)) { + await fs.delete(outputLogPath) } await zipUtil.removeTmpFiles(zipMetadata) session.artifactsUploadDuration = performance.now() - uploadStartTime diff --git a/packages/core/src/codewhisperer/models/constants.ts b/packages/core/src/codewhisperer/models/constants.ts index f19a3cb6349..74b2c54f82d 100644 --- a/packages/core/src/codewhisperer/models/constants.ts +++ b/packages/core/src/codewhisperer/models/constants.ts @@ -725,6 +725,8 @@ export const noOpenProjectsFoundChatTestGenMessage = `Sorry, I couldn\'t find a export const unitTestGenerationCancelMessage = 'Unit test generation cancelled.' +export const tooManyRequestErrorMessage = 'Too many requests. Please wait before retrying.' + export const noJavaProjectsFoundChatMessage = `I couldn\'t find a project that I can upgrade. Currently, I support Java 8, Java 11, and Java 17 projects built on Maven. Make sure your project is open in the IDE. For more information, see the [Amazon Q documentation](${codeTransformPrereqDoc}).` export const linkToDocsHome = 'https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/code-transformation.html' @@ -860,7 +862,7 @@ export enum TestGenerationJobStatus { COMPLETED = 'COMPLETED', } -export enum ZipUseCase { +export enum FeatureUseCase { TEST_GENERATION = 'TEST_GENERATION', CODE_SCAN = 'CODE_SCAN', } diff --git a/packages/core/src/codewhisperer/service/codeFixHandler.ts b/packages/core/src/codewhisperer/service/codeFixHandler.ts index 0358d8d3ed9..b707ee01583 100644 --- a/packages/core/src/codewhisperer/service/codeFixHandler.ts +++ b/packages/core/src/codewhisperer/service/codeFixHandler.ts @@ -34,7 +34,7 @@ export async function getPresignedUrlAndUpload( getLogger().verbose(`CreateUploadUrlRequest requestId: ${srcResp.$response.requestId}`) getLogger().verbose(`Complete Getting presigned Url for uploading src context.`) getLogger().verbose(`Uploading src context...`) - await uploadArtifactToS3(zipFilePath, srcResp) + await uploadArtifactToS3(zipFilePath, srcResp, CodeWhispererConstants.FeatureUseCase.CODE_SCAN) getLogger().verbose(`Complete uploading src context.`) const artifactMap: ArtifactMap = { SourceCode: srcResp.uploadId, diff --git a/packages/core/src/codewhisperer/service/securityScanHandler.ts b/packages/core/src/codewhisperer/service/securityScanHandler.ts index ba70e558733..ab9e637e519 100644 --- a/packages/core/src/codewhisperer/service/securityScanHandler.ts +++ b/packages/core/src/codewhisperer/service/securityScanHandler.ts @@ -43,6 +43,8 @@ import { getTelemetryReasonDesc } from '../../shared/errors' import { CodeWhispererSettings } from '../util/codewhispererSettings' import { detectCommentAboveLine } from '../../shared/utilities/commentUtils' import { runtimeLanguageContext } from '../util/runtimeLanguageContext' +import { FeatureUseCase } from '../models/constants' +import { UploadTestArtifactToS3Error } from '../../amazonqTest/error' export async function listScanResults( client: DefaultCodeWhispererClient, @@ -287,7 +289,7 @@ export async function getPresignedUrlAndUpload( logger.verbose(`CreateUploadUrlRequest request id: ${srcResp.$response.requestId}`) logger.verbose(`Complete Getting presigned Url for uploading src context.`) logger.verbose(`Uploading src context...`) - await uploadArtifactToS3(zipMetadata.zipFilePath, srcResp, scope) + await uploadArtifactToS3(zipMetadata.zipFilePath, srcResp, FeatureUseCase.CODE_SCAN, scope) logger.verbose(`Complete uploading src context.`) const artifactMap: ArtifactMap = { SourceCode: srcResp.uploadId, @@ -343,6 +345,7 @@ export function throwIfCancelled(scope: CodeWhispererConstants.CodeAnalysisScope export async function uploadArtifactToS3( fileName: string, resp: CreateUploadUrlResponse, + featureUseCase: FeatureUseCase, scope?: CodeWhispererConstants.CodeAnalysisScope ) { const logger = getLoggerForScope(scope) @@ -365,14 +368,23 @@ export async function uploadArtifactToS3( }).response logger.debug(`StatusCode: ${response.status}, Text: ${response.statusText}`) } catch (error) { + let errorMessage = '' + const isCodeScan = featureUseCase === FeatureUseCase.CODE_SCAN + const featureType = isCodeScan ? 'security scans' : 'unit test generation' + const defaultMessage = isCodeScan ? 'Security scan failed.' : 'Test generation failed.' getLogger().error( - `Amazon Q is unable to upload workspace artifacts to Amazon S3 for security scans. For more information, see the Amazon Q documentation or contact your network or organization administrator.` + `Amazon Q is unable to upload workspace artifacts to Amazon S3 for ${featureType}. ` + + 'For more information, see the Amazon Q documentation or contact your network or organization administrator.' ) - const errorMessage = getTelemetryReasonDesc(error)?.includes(`"PUT" request failed with code "403"`) - ? `"PUT" request failed with code "403"` - : (getTelemetryReasonDesc(error) ?? 'Security scan failed.') - - throw new UploadArtifactToS3Error(errorMessage) + const errorDesc = getTelemetryReasonDesc(error) + if (errorDesc?.includes('"PUT" request failed with code "403"')) { + errorMessage = '"PUT" request failed with code "403"' + } else if (errorDesc?.includes('"PUT" request failed with code "503"')) { + errorMessage = '"PUT" request failed with code "503"' + } else { + errorMessage = errorDesc ?? defaultMessage + } + throw isCodeScan ? new UploadArtifactToS3Error(errorMessage) : new UploadTestArtifactToS3Error(errorMessage) } } diff --git a/packages/core/src/codewhisperer/service/testGenHandler.ts b/packages/core/src/codewhisperer/service/testGenHandler.ts index ee2de612b77..bf8bd3507bb 100644 --- a/packages/core/src/codewhisperer/service/testGenHandler.ts +++ b/packages/core/src/codewhisperer/service/testGenHandler.ts @@ -13,7 +13,15 @@ import CodeWhispererUserClient, { CreateUploadUrlRequest, TargetCode, } from '../client/codewhispereruserclient' -import { CreateUploadUrlError, InvalidSourceZipError, TestGenFailedError, TestGenTimedOutError } from '../models/errors' +import { + CreateTestJobError, + CreateUploadUrlError, + ExportResultsArchiveError, + InvalidSourceZipError, + TestGenFailedError, + TestGenStoppedError, + TestGenTimedOutError, +} from '../../amazonqTest/error' import { getMd5, uploadArtifactToS3 } from './securityScanHandler' import { fs, randomUUID, sleep, tempDirPath } from '../../shared' import { ShortAnswer, TestGenerationJobStatus, testGenState } from '..' @@ -29,7 +37,7 @@ import { glob } from 'glob' export function throwIfCancelled() { // TODO: fileName will be '' if user gives propt without opening if (testGenState.isCancelling()) { - throw Error(CodeWhispererConstants.unitTestGenerationCancelMessage) + throw new TestGenStoppedError() } } @@ -47,12 +55,12 @@ export async function getPresignedUrlAndUploadTestGen(zipMetadata: ZipMetadata) logger.verbose(`Prepare for uploading src context...`) const srcResp = await codeWhisperer.codeWhispererClient.createUploadUrl(srcReq).catch((err) => { getLogger().error(`Failed getting presigned url for uploading src context. Request id: ${err.requestId}`) - throw new CreateUploadUrlError(err) + throw new CreateUploadUrlError(err.message) }) logger.verbose(`CreateUploadUrlRequest requestId: ${srcResp.$response.requestId}`) logger.verbose(`Complete Getting presigned Url for uploading src context.`) logger.verbose(`Uploading src context...`) - await uploadArtifactToS3(zipMetadata.zipFilePath, srcResp) + await uploadArtifactToS3(zipMetadata.zipFilePath, srcResp, CodeWhispererConstants.FeatureUseCase.TEST_GENERATION) logger.verbose(`Complete uploading src context.`) const artifactMap: ArtifactMap = { SourceCode: srcResp.uploadId, @@ -96,7 +104,7 @@ export async function createTestJob( const resp = await codewhispererClient.codeWhispererClient.startTestGeneration(req).catch((err) => { ChatSessionManager.Instance.getSession().startTestGenerationRequestId = err.requestId logger.error(`Failed creating test job. Request id: ${err.requestId}`) - throw err + throw new CreateTestJobError(err.message) }) logger.info('Unit test generation request id: %s', resp.$response.requestId) logger.debug('Unit test generation data: %O', resp.$response.data) @@ -252,7 +260,7 @@ export async function exportResultsArchive( session.numberOfTestsGenerated = 0 downloadErrorMessage = (e as Error).message getLogger().error(`Unit Test Generation: ExportResultArchive error = ${downloadErrorMessage}`) - throw new Error('Error downloading test generation result artifacts: ' + downloadErrorMessage) + throw new ExportResultsArchiveError(downloadErrorMessage) } } @@ -291,7 +299,7 @@ export async function downloadResultArchive( } catch (e: any) { downloadErrorMessage = (e as Error).message getLogger().error(`Unit Test Generation: ExportResultArchive error = ${downloadErrorMessage}`) - throw e + throw new ExportResultsArchiveError(downloadErrorMessage) } finally { cwStreamingClient.destroy() } diff --git a/packages/core/src/codewhisperer/util/zipUtil.ts b/packages/core/src/codewhisperer/util/zipUtil.ts index ab938aeb643..64a9ccc3b8d 100644 --- a/packages/core/src/codewhisperer/util/zipUtil.ts +++ b/packages/core/src/codewhisperer/util/zipUtil.ts @@ -20,9 +20,10 @@ import { NoSourceFilesError, ProjectSizeExceededError, } from '../models/errors' -import { ZipUseCase } from '../models/constants' +import { FeatureUseCase } from '../models/constants' import { ChildProcess, ChildProcessOptions } from '../../shared/utilities/processUtils' import { removeAnsi } from '../../shared' +import { ProjectZipError } from '../../amazonqTest/error' export interface ZipMetadata { rootDir: string @@ -135,7 +136,7 @@ export class ZipUtil { if (this.reachSizeLimit(this._totalSize, scope)) { throw new FileSizeExceededError() } - const zipFilePath = this.getZipDirPath(ZipUseCase.CODE_SCAN) + CodeWhispererConstants.codeScanZipExt + const zipFilePath = this.getZipDirPath(FeatureUseCase.CODE_SCAN) + CodeWhispererConstants.codeScanZipExt zip.writeZip(zipFilePath) return zipFilePath } @@ -203,15 +204,15 @@ export class ZipUtil { await processDirectory(metadataDir) } - protected async zipProject(useCase: ZipUseCase, projectPath?: string, metadataDir?: string) { + protected async zipProject(useCase: FeatureUseCase, projectPath?: string, metadataDir?: string) { const zip = new admZip() let projectPaths = [] - if (useCase === ZipUseCase.TEST_GENERATION && projectPath) { + if (useCase === FeatureUseCase.TEST_GENERATION && projectPath) { projectPaths.push(projectPath) } else { projectPaths = this.getProjectPaths() } - if (useCase === ZipUseCase.CODE_SCAN) { + if (useCase === FeatureUseCase.CODE_SCAN) { await this.processCombinedGitDiff(zip, projectPaths, '', CodeWhispererConstants.CodeAnalysisScope.PROJECT) } const languageCount = new Map() @@ -220,7 +221,7 @@ export class ZipUtil { if (metadataDir) { await this.processMetadataDir(zip, metadataDir) } - if (useCase !== ZipUseCase.TEST_GENERATION) { + if (useCase !== FeatureUseCase.TEST_GENERATION) { this.processOtherFiles(zip, languageCount) } @@ -403,7 +404,7 @@ export class ZipUtil { zip: admZip, languageCount: Map, projectPaths: string[] | undefined, - useCase: ZipUseCase + useCase: FeatureUseCase ) { if (!projectPaths || projectPaths.length === 0) { return @@ -420,7 +421,7 @@ export class ZipUtil { const zipEntryPath = this.getZipEntryPath(projectName, file.relativeFilePath) if (ZipConstants.knownBinaryFileExts.includes(path.extname(file.fileUri.fsPath))) { - if (useCase === ZipUseCase.TEST_GENERATION) { + if (useCase === FeatureUseCase.TEST_GENERATION) { continue } await this.processBinaryFile(zip, file.fileUri, zipEntryPath) @@ -511,10 +512,10 @@ export class ZipUtil { return vscode.workspace.textDocuments.some((document) => document.uri.fsPath === uri.fsPath && document.isDirty) } - public getZipDirPath(useCase: ZipUseCase): string { + public getZipDirPath(useCase: FeatureUseCase): string { if (this._zipDir === '') { const prefix = - useCase === ZipUseCase.TEST_GENERATION + useCase === FeatureUseCase.TEST_GENERATION ? CodeWhispererConstants.TestGenerationTruncDirPrefix : CodeWhispererConstants.codeScanTruncDirPrefix @@ -528,7 +529,7 @@ export class ZipUtil { scope: CodeWhispererConstants.CodeAnalysisScope ): Promise { try { - const zipDirPath = this.getZipDirPath(ZipUseCase.CODE_SCAN) + const zipDirPath = this.getZipDirPath(FeatureUseCase.CODE_SCAN) let zipFilePath: string if ( scope === CodeWhispererConstants.CodeAnalysisScope.FILE_AUTO || @@ -536,7 +537,7 @@ export class ZipUtil { ) { zipFilePath = await this.zipFile(uri, scope) } else if (scope === CodeWhispererConstants.CodeAnalysisScope.PROJECT) { - zipFilePath = await this.zipProject(ZipUseCase.CODE_SCAN) + zipFilePath = await this.zipProject(FeatureUseCase.CODE_SCAN) } else { throw new ToolkitError(`Unknown code analysis scope: ${scope}`) } @@ -562,7 +563,7 @@ export class ZipUtil { public async generateZipTestGen(projectPath: string, initialExecution: boolean): Promise { try { // const repoMapFile = await LspClient.instance.getRepoMapJSON() - const zipDirPath = this.getZipDirPath(ZipUseCase.TEST_GENERATION) + const zipDirPath = this.getZipDirPath(FeatureUseCase.TEST_GENERATION) const metadataDir = path.join(zipDirPath, 'utgRequiredArtifactsDir') @@ -590,7 +591,7 @@ export class ZipUtil { } } - const zipFilePath: string = await this.zipProject(ZipUseCase.TEST_GENERATION, projectPath, metadataDir) + const zipFilePath: string = await this.zipProject(FeatureUseCase.TEST_GENERATION, projectPath, metadataDir) const zipFileSize = (await fs.stat(zipFilePath)).size return { rootDir: zipDirPath, @@ -604,7 +605,9 @@ export class ZipUtil { } } catch (error) { getLogger().error('Zip error caused by: %s', error) - throw error + throw new ProjectZipError( + error instanceof Error ? error.message : 'Unknown error occurred during zip operation' + ) } } // TODO: Refactor this