diff --git a/packages/amazonq/.changes/next-release/Feature-e3c11418-cbdc-4281-ab70-b1a7c574aaa1.json b/packages/amazonq/.changes/next-release/Feature-e3c11418-cbdc-4281-ab70-b1a7c574aaa1.json new file mode 100644 index 0000000000..c0efd32b02 --- /dev/null +++ b/packages/amazonq/.changes/next-release/Feature-e3c11418-cbdc-4281-ab70-b1a7c574aaa1.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "Feature(Amazon Q Code Transformation): support conversions of embedded SQL from Oracle to PostgreSQL" +} diff --git a/packages/core/src/amazonq/webview/ui/tabs/constants.ts b/packages/core/src/amazonq/webview/ui/tabs/constants.ts index 8696274c69..1eb2783a4e 100644 --- a/packages/core/src/amazonq/webview/ui/tabs/constants.ts +++ b/packages/core/src/amazonq/webview/ui/tabs/constants.ts @@ -2,7 +2,6 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ -import { isSQLTransformReady } from '../../../../dev/config' import { TabType } from '../storages/tabsStorage' export type TabTypeData = { @@ -34,16 +33,6 @@ What would you like to work on?`, gumby: { title: 'Q - Code Transformation', placeholder: 'Open a new tab to chat with Q', - welcome: isSQLTransformReady - ? `Welcome to code transformation! - -I can help you with the following tasks: -- Upgrade your Java 8 and Java 11 codebases to Java 17 -- Convert embedded SQL from Oracle databases to PostgreSQL - -What would you like to do? You can enter 'language upgrade' or 'SQL conversion'.` - : `Welcome to code transformation! - -I can help you upgrade your Java 8 and 11 codebases to Java 17.`, + welcome: 'Welcome to Code Transformation!', }, } diff --git a/packages/core/src/amazonqGumby/chat/controller/controller.ts b/packages/core/src/amazonqGumby/chat/controller/controller.ts index 1a9cd74bb7..37d139f5f1 100644 --- a/packages/core/src/amazonqGumby/chat/controller/controller.ts +++ b/packages/core/src/amazonqGumby/chat/controller/controller.ts @@ -33,7 +33,7 @@ import { getValidSQLConversionCandidateProjects, validateSQLMetadataFile, } from '../../../codewhisperer/commands/startTransformByQ' -import { JDKVersion, transformByQState } from '../../../codewhisperer/models/model' +import { JDKVersion, TransformationCandidateProject, transformByQState } from '../../../codewhisperer/models/model' import { AbsolutePathDetectedError, AlternateDependencyVersionsNotFoundError, @@ -62,7 +62,6 @@ import { getStringHash } from '../../../shared/utilities/textUtilities' import { getVersionData } from '../../../codewhisperer/service/transformByQ/transformMavenHandler' import AdmZip from 'adm-zip' import { AuthError } from '../../../auth/sso/server' -import { isSQLTransformReady } from '../../../dev/config' // These events can be interactions within the chat, // or elsewhere in the IDE @@ -190,12 +189,31 @@ export class GumbyController { } private async transformInitiated(message: any) { - // feature flag for SQL transformations - if (!isSQLTransformReady) { + // silently check for projects eligible for SQL conversion + let embeddedSQLProjects: TransformationCandidateProject[] = [] + try { + embeddedSQLProjects = await getValidSQLConversionCandidateProjects() + } catch (err) { + getLogger().error(`CodeTransformation: error validating SQL conversion projects: ${err}`) + } + + if (embeddedSQLProjects.length === 0) { await this.handleLanguageUpgrade(message) return } + let javaUpgradeProjects: TransformationCandidateProject[] = [] + try { + javaUpgradeProjects = await getValidLanguageUpgradeCandidateProjects() + } catch (err) { + getLogger().error(`CodeTransformation: error validating Java upgrade projects: ${err}`) + } + + if (javaUpgradeProjects.length === 0) { + await this.handleSQLConversion(message) + return + } + // if previous transformation was already running, show correct message to user switch (this.sessionStorage.getSession().conversationState) { case ConversationState.JOB_SUBMITTED: @@ -224,7 +242,10 @@ export class GumbyController { this.sessionStorage.getSession().conversationState = ConversationState.WAITING_FOR_TRANSFORMATION_OBJECTIVE this.messenger.sendStaticTextResponse('choose-transformation-objective', message.tabID) this.messenger.sendChatInputEnabled(message.tabID, true) - this.messenger.sendUpdatePlaceholder(message.tabID, "Enter 'language upgrade' or 'SQL conversion'") + this.messenger.sendUpdatePlaceholder( + message.tabID, + CodeWhispererConstants.chooseTransformationObjectivePlaceholder + ) } private async beginTransformation(message: any) { @@ -310,13 +331,7 @@ export class GumbyController { private async validateSQLConversionProjects(message: any) { try { - const validProjects = await telemetry.codeTransform_validateProject.run(async () => { - telemetry.record({ - codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId(), - }) - const validProjects = await getValidSQLConversionCandidateProjects() - return validProjects - }) + const validProjects = await getValidSQLConversionCandidateProjects() return validProjects } catch (e: any) { if (e instanceof NoJavaProjectsFoundError) { @@ -624,6 +639,14 @@ export class GumbyController { case ConversationState.WAITING_FOR_TRANSFORMATION_OBJECTIVE: { const objective = data.message.trim().toLowerCase() + // since we're prompting the user, their project(s) must be eligible for both types of transformations, so track how often this happens here + if (objective === 'language upgrade' || objective === 'sql conversion') { + telemetry.codeTransform_submitSelection.emit({ + codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId(), + userChoice: objective, + result: 'Succeeded', + }) + } if (objective === 'language upgrade') { await this.handleLanguageUpgrade(data) } else if (objective === 'sql conversion') { diff --git a/packages/core/src/amazonqGumby/chat/controller/messenger/messenger.ts b/packages/core/src/amazonqGumby/chat/controller/messenger/messenger.ts index 69f0a48cb6..cc4431faca 100644 --- a/packages/core/src/amazonqGumby/chat/controller/messenger/messenger.ts +++ b/packages/core/src/amazonqGumby/chat/controller/messenger/messenger.ts @@ -252,7 +252,7 @@ export class Messenger { formItems.push({ id: 'GumbyTransformSQLConversionProjectForm', type: 'select', - title: 'Choose a project to transform', + title: CodeWhispererConstants.chooseProjectFormTitle, mandatory: true, options: projectFormOptions, }) @@ -260,7 +260,7 @@ export class Messenger { formItems.push({ id: 'GumbyTransformSQLSchemaForm', type: 'select', - title: 'Choose the schema of the database', + title: CodeWhispererConstants.chooseSchemaFormTitle, mandatory: true, options: Array.from(transformByQState.getSchemaOptions()).map((schema) => ({ value: schema, @@ -271,7 +271,7 @@ export class Messenger { this.dispatcher.sendAsyncEventProgress( new AsyncEventProgressMessage(tabID, { inProgress: true, - message: 'I can convert your embedded SQL, but I need some more info from you first.', + message: CodeWhispererConstants.chooseProjectSchemaFormMessage, }) ) @@ -390,7 +390,7 @@ export class Messenger { message = 'I will continue transforming your code without upgrading this dependency.' break case 'choose-transformation-objective': - message = 'Choose your transformation objective.' + message = CodeWhispererConstants.chooseTransformationObjective break } @@ -422,6 +422,7 @@ export class Messenger { message = CodeWhispererConstants.noJavaProjectsFoundChatMessage break case 'no-maven-java-project-found': + // shown when user has no pom.xml, but at this point also means they have no eligible SQL conversion projects message = CodeWhispererConstants.noPomXmlFoundChatMessage break case 'could-not-compile-project': @@ -447,23 +448,7 @@ export class Messenger { break } - const buttons: ChatItemButton[] = [] - buttons.push({ - keepCardAfterClick: false, - text: CodeWhispererConstants.startTransformationButtonText, - id: ButtonActions.CONFIRM_START_TRANSFORMATION_FLOW, - }) - - this.dispatcher.sendChatMessage( - new ChatMessage( - { - message, - messageType: 'ai-prompt', - buttons, - }, - tabID - ) - ) + this.sendJobFinishedMessage(tabID, message) } /** diff --git a/packages/core/src/amazonqGumby/models/constants.ts b/packages/core/src/amazonqGumby/models/constants.ts index 1478745497..87f9d45669 100644 --- a/packages/core/src/amazonqGumby/models/constants.ts +++ b/packages/core/src/amazonqGumby/models/constants.ts @@ -7,6 +7,6 @@ export const gumbyChat = 'gumbyChat' // This sets the tab name -export const featureName = 'Q - Code Transform' +export const featureName = 'Q - Code Transformation' export const dependencyNoAvailableVersions = 'no available versions' diff --git a/packages/core/src/codewhisperer/commands/startTransformByQ.ts b/packages/core/src/codewhisperer/commands/startTransformByQ.ts index d670aea908..4a2465dc69 100644 --- a/packages/core/src/codewhisperer/commands/startTransformByQ.ts +++ b/packages/core/src/codewhisperer/commands/startTransformByQ.ts @@ -20,6 +20,7 @@ import { TransformByQStatus, DB, TransformationType, + TransformationCandidateProject, } from '../models/model' import { createZipManifest, @@ -80,6 +81,8 @@ import { setContext } from '../../shared/vscode/setContext' import { makeTemporaryToolkitFolder } from '../../shared' import globals from '../../shared/extensionGlobals' import { convertDateToTimestamp } from '../../shared/datetime' +import { isWin } from '../../shared/vscode/env' +import { ChildProcess } from '../../shared/utilities/processUtils' function getFeedbackCommentData() { const jobId = transformByQState.getJobId() @@ -150,7 +153,7 @@ export async function validateSQLMetadataFile(fileContents: string, message: any } export async function setMaven() { - let mavenWrapperExecutableName = os.platform() === 'win32' ? 'mvnw.cmd' : 'mvnw' + let mavenWrapperExecutableName = isWin() ? 'mvnw.cmd' : 'mvnw' const mavenWrapperExecutablePath = path.join(transformByQState.getProjectPath(), mavenWrapperExecutableName) if (fs.existsSync(mavenWrapperExecutablePath)) { if (mavenWrapperExecutableName === 'mvnw') { @@ -728,13 +731,57 @@ export async function finalizeTransformationJob(status: string) { export async function getValidLanguageUpgradeCandidateProjects() { const openProjects = await getOpenProjects() const javaMavenProjects = await validateOpenProjects(openProjects) + getLogger().info(`CodeTransformation: found ${javaMavenProjects.length} projects eligible for language upgrade`) return javaMavenProjects } export async function getValidSQLConversionCandidateProjects() { - const openProjects = await getOpenProjects() - const javaProjects = await getJavaProjects(openProjects) - return javaProjects + const embeddedSQLProjects: TransformationCandidateProject[] = [] + await telemetry.codeTransform_validateProject.run(async () => { + telemetry.record({ + codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId(), + }) + const openProjects = await getOpenProjects() + const javaProjects = await getJavaProjects(openProjects) + let errorLog = '' + const isWindows = isWin() + const command = isWindows ? 'findstr' : 'grep' + for (const project of javaProjects) { + // as long as at least one of these strings is found, project contains embedded SQL statements + const searchStrings = ['oracle.jdbc.OracleDriver', 'jdbc:oracle:thin:@//', 'jdbc:oracle:oci:@//'] + for (const str of searchStrings) { + // case-insensitive, recursive search + const args = isWindows ? ['/i', '/s', str, '*.*'] : ['-i', '-r', str] + const spawnResult = await new ChildProcess(command, args).run({ + spawnOptions: { cwd: project.path, shell: false }, + }) + // just for telemetry purposes + if (spawnResult.error || spawnResult.stderr) { + errorLog += `${JSON.stringify(spawnResult)}---` + } else { + errorLog += `${command} succeeded: ${spawnResult.exitCode}` + } + /* + note: + error is set when command fails to spawn; stderr is set when command itself fails. + status of 0 means search string was detected (will be printed to stdout). + status will be non-zero and stdout / stderr / error will be empty when search string is not detected. + */ + getLogger().info(`CodeTransformation: searching for ${str} in ${project.path}, result = ${errorLog}`) + if (spawnResult.exitCode === 0) { + embeddedSQLProjects.push(project) + break + } + } + } + getLogger().info( + `CodeTransformation: found ${embeddedSQLProjects.length} projects with embedded SQL statements` + ) + telemetry.record({ + codeTransformMetadata: errorLog, + }) + }) + return embeddedSQLProjects } export async function setTransformationToRunningState() { diff --git a/packages/core/src/codewhisperer/models/constants.ts b/packages/core/src/codewhisperer/models/constants.ts index f9c3e3e1c4..e8f912687d 100644 --- a/packages/core/src/codewhisperer/models/constants.ts +++ b/packages/core/src/codewhisperer/models/constants.ts @@ -450,6 +450,10 @@ export const codeTransformLocThreshold = 100000 export const jobStartedChatMessage = 'I am 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. If I run into any issues, I might pause the transformation to get input from you on how to proceed.' +export const chooseTransformationObjective = `I can help you with the following tasks:\n- Upgrade your Java 8 and Java 11 codebases to Java 17, or upgrade Java 17 code with up to date libraries and other dependencies.\n- Convert embedded SQL code for Oracle to PostgreSQL database migrations in AWS DMS.\n\nWhat would you like to do? You can enter "language upgrade" or "sql conversion".` + +export const chooseTransformationObjectivePlaceholder = 'Enter "language upgrade" or "sql conversion"' + export const uploadingCodeStepMessage = 'Upload your code' export const buildCodeStepMessage = 'Build uploaded code in secure build environment' @@ -477,6 +481,8 @@ export const failedStepMessage = 'The step failed, fetching additional details.. export const jobCompletedMessage = 'The transformation completed.' +export const noChangesMadeMessage = "I didn't make any changes for this transformation." + export const noOngoingJobMessage = 'No ongoing job.' export const nothingToShowMessage = 'Nothing to show' @@ -490,8 +496,7 @@ export const startTransformationButtonText = 'Start a new transformation' export const stopTransformationButtonText = 'Stop transformation' -export const checkingForProjectsChatMessage = - 'I am checking for open projects that are eligible for Code Transformation.' +export const checkingForProjectsChatMessage = 'Checking for eligible projects...' export const buildStartedChatMessage = 'I am building your project. This can take up to 10 minutes, depending on the size of your project.' @@ -507,7 +512,7 @@ export const absolutePathDetectedMessage = (numPaths: number, buildFile: string, export const unsupportedJavaVersionChatMessage = `Sorry, currently I can only upgrade Java 8 or Java 11 projects. For more information, see the [Amazon Q documentation](${codeTransformPrereqDoc}).` export const selectSQLMetadataFileHelpMessage = - 'Next, I need the zipped metadata file from your schema conversion. You can download the metadata by going to your migration project in the AWS DMS console. Open the schema conversion and choose **Convert the embedded SQL in your application**. You can downloaded the metadata from Amazon S3 in the {schema-conversion-project}/ directory.' + 'Okay, I can convert the embedded SQL code for your Oracle to PostgreSQL transformation. To get started, upload the zipped metadata file from your schema conversion in AWS Data Migration Service (DMS). To retrieve the metadata file:\n1. Open your database migration project in the AWS DMS console.\n2. Open the schema conversion and choose **Convert the embedded SQL in your application**.\n3. Choose the link to Amazon S3 console.\n\nYou can download the metadata file from the {schema-conversion-project}/ directory. For more info, refer to the [documentation](https://docs.aws.amazon.com/dms/latest/userguide/schema-conversion-save-apply.html#schema-conversion-save).' export const invalidMetadataFileUnsupportedSourceDB = 'I can only convert SQL for migrations from an Oracle source database. The provided .sct file indicates another source database for this migration.' @@ -578,21 +583,15 @@ export const jobCancelledChatMessage = export const jobCancelledNotification = 'You cancelled the transformation.' -export const jobCompletedChatMessage = - 'I upgraded your code. 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 jobCompletedChatMessage = `I transformed your code. 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 jobCompletedNotification = - 'Amazon Q upgraded your code. 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 jobCompletedNotification = `Amazon Q transformed your code. 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 jobPartiallyCompletedChatMessage = - 'I upgraded part of your code. 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 jobPartiallyCompletedChatMessage = `I transformed part of your code. 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 jobPartiallyCompletedNotification = - 'Amazon Q upgraded part of your code. 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 jobPartiallyCompletedNotification = `Amazon Q transformed part of your code. 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 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](${codeTransformPrereqDoc}).` - -export const noPomXmlFoundNotification = `None of your open projects are supported by Amazon Q Code Transformation. Amazon Q could not 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](${codeTransformPrereqDoc}).` +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, nor could I find any embedded SQL statements. Currently, I can only upgrade Java 8 or Java 11 projects built on Maven, or Oracle SQL to PostgreSQL in Java projects. For more information, see the [Amazon Q documentation](${codeTransformPrereqDoc}).` export const noJavaHomeFoundChatMessage = `Sorry, I couldn\'t locate your Java installation. For more information, see the [Amazon Q documentation](${codeTransformPrereqDoc}).` @@ -630,9 +629,9 @@ export const changesAppliedChatMessage = 'I applied the changes to your project. export const changesAppliedNotification = 'Amazon Q applied the changes to your project.' -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](${codeTransformPrereqDoc}).` +export const noOpenProjectsFoundChatMessage = `I couldn't find a project that I can transform. Please open a Java project and try again. For more information, see the [Amazon Q documentation](${codeTransformPrereqDoc}).` -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](${codeTransformPrereqDoc}).` +export const noJavaProjectsFoundChatMessage = `I couldn't find a project that I can transform. Please open a Java project and try again. 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' @@ -680,6 +679,10 @@ export const chooseSourceVersionFormTitle = 'Choose the source code version' export const chooseTargetVersionFormTitle = 'Choose the target code version' +export const chooseSchemaFormTitle = 'Choose the schema of the database' + +export const chooseProjectSchemaFormMessage = 'To continue, choose the project and schema for this transformation.' + export const skipUnitTestsFormTitle = 'Choose to skip unit tests' export const skipUnitTestsFormMessage = diff --git a/packages/core/src/codewhisperer/models/model.ts b/packages/core/src/codewhisperer/models/model.ts index f82ca81def..b066a78ba0 100644 --- a/packages/core/src/codewhisperer/models/model.ts +++ b/packages/core/src/codewhisperer/models/model.ts @@ -328,7 +328,7 @@ export class ZipManifest { buildLogs: string = 'build-logs.txt' version: string = '1.0' hilCapabilities: string[] = ['HIL_1pDependency_VersionUpgrade'] - transformCapabilities: string[] = ['EXPLAINABILITY_V1'] // TO-DO: for SQL conversions, maybe make this = [] + transformCapabilities: string[] = ['EXPLAINABILITY_V1'] customBuildCommand: string = 'clean test' requestedConversions?: { sqlConversion?: { diff --git a/packages/core/src/codewhisperer/service/transformByQ/transformApiHandler.ts b/packages/core/src/codewhisperer/service/transformByQ/transformApiHandler.ts index 94b7488fd2..dc599a1dcd 100644 --- a/packages/core/src/codewhisperer/service/transformByQ/transformApiHandler.ts +++ b/packages/core/src/codewhisperer/service/transformByQ/transformApiHandler.ts @@ -232,6 +232,8 @@ export async function uploadPayload(payloadFileName: string, uploadContext?: Upl */ const mavenExcludedExtensions = ['.repositories', '.sha1'] +const sourceExcludedExtensions = ['.DS_Store'] + /** * Determines if the specified file path corresponds to a Maven metadata file * by checking against known metadata file extensions. This is used to identify @@ -244,24 +246,22 @@ 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 - * dependencies. If isDependenciesFolder is true, then we are getting all the files - * of the dependencies which were copied over by the previously-run mvn command, in which case - * we DO want to include any dependencies that may happen to be named "target", hence the check - * in the first part of the IF statement. The point of excluding folders named target is that - * "target" is also the name of the folder where .class files, large JARs, etc. are stored after - * building, and we do not want these included in the ZIP so we exclude these when calling - * getFilesRecursively on the source code folder. - */ -function getFilesRecursively(dir: string, isDependenciesFolder: boolean): string[] { +// do not zip the .DS_Store file as it may appear in the diff.patch +function isExcludedSourceFile(path: string): boolean { + return sourceExcludedExtensions.some((extension) => path.endsWith(extension)) +} + +// zip all dependency files and all source files excluding "target" (contains large JARs) plus ".git" and ".idea" (may appear in diff.patch) +export function getFilesRecursively(dir: string, isDependenciesFolder: boolean): string[] { const entries = nodefs.readdirSync(dir, { withFileTypes: true }) const files = entries.flatMap((entry) => { const res = path.resolve(dir, entry.name) - // exclude 'target' directory from ZIP (except if zipping dependencies) due to issues in backend if (entry.isDirectory()) { - if (isDependenciesFolder || entry.name !== 'target') { + if (isDependenciesFolder) { + // include all dependency files + return getFilesRecursively(res, isDependenciesFolder) + } else if (entry.name !== 'target' && entry.name !== '.git' && entry.name !== '.idea') { + // exclude the above directories when zipping source code return getFilesRecursively(res, isDependenciesFolder) } else { return [] @@ -310,8 +310,8 @@ export async function zipCode( const sourceFiles = getFilesRecursively(projectPath, false) let sourceFilesSize = 0 for (const file of sourceFiles) { - if (nodefs.statSync(file).isDirectory()) { - getLogger().info('CodeTransformation: Skipping directory, likely a symlink') + if (nodefs.statSync(file).isDirectory() || isExcludedSourceFile(file)) { + getLogger().info('CodeTransformation: Skipping file') continue } const relativePath = path.relative(projectPath, file) @@ -334,16 +334,13 @@ export async function zipCode( target: transformByQState.getTargetDB(), schema: transformByQState.getSchema(), host: transformByQState.getSourceServerName(), - sctFileName: metadataZip.getEntries().filter((entry) => entry.entryName.endsWith('.sct'))[0] - .entryName, + sctFileName: metadataZip.getEntries().filter((entry) => entry.name.endsWith('.sct'))[0].name, }, } // TO-DO: later consider making this add to path.join(zipManifest.dependenciesRoot, 'qct-sct-metadata', entry.entryName) so that it's more organized metadataZip .getEntries() - .forEach((entry) => - zip.addFile(path.join(zipManifest.dependenciesRoot, entry.entryName), entry.getData()) - ) + .forEach((entry) => zip.addFile(path.join(zipManifest.dependenciesRoot, entry.name), entry.getData())) const sqlMetadataSize = (await nodefs.promises.stat(transformByQState.getMetadataPathSQL())).size getLogger().info(`CodeTransformation: SQL metadata file size = ${sqlMetadataSize}`) } diff --git a/packages/core/src/codewhisperer/service/transformByQ/transformationResultsViewProvider.ts b/packages/core/src/codewhisperer/service/transformByQ/transformationResultsViewProvider.ts index eab339df71..8bea3486bd 100644 --- a/packages/core/src/codewhisperer/service/transformByQ/transformationResultsViewProvider.ts +++ b/packages/core/src/codewhisperer/service/transformByQ/transformationResultsViewProvider.ts @@ -145,6 +145,12 @@ export class DiffModel { */ public parseDiff(pathToDiff: string, pathToWorkspace: string): ProposedChangeNode[] { const diffContents = fs.readFileSync(pathToDiff, 'utf8') + + if (!diffContents.trim()) { + getLogger().error(`CodeTransformation: diff.patch file is empty`) + throw new Error(CodeWhispererConstants.noChangesMadeMessage) + } + const changedFiles = parsePatch(diffContents) // path to the directory containing copy of the changed files in the transformed project const pathToTmpSrcDir = this.copyProject(pathToWorkspace, changedFiles) diff --git a/packages/core/src/dev/config.ts b/packages/core/src/dev/config.ts index 276732a72b..d5fa49b242 100644 --- a/packages/core/src/dev/config.ts +++ b/packages/core/src/dev/config.ts @@ -10,6 +10,3 @@ export const betaUrl = { amazonq: '', toolkit: '', } - -// feature flag for SQL transformations -export const isSQLTransformReady = false diff --git a/packages/core/src/test/codewhisperer/commands/transformByQ.test.ts b/packages/core/src/test/codewhisperer/commands/transformByQ.test.ts index 9aee508cf7..b294d07b58 100644 --- a/packages/core/src/test/codewhisperer/commands/transformByQ.test.ts +++ b/packages/core/src/test/codewhisperer/commands/transformByQ.test.ts @@ -32,6 +32,7 @@ import { updateJobHistory, zipCode, getTableMapping, + getFilesRecursively, } from '../../../codewhisperer/service/transformByQ/transformApiHandler' import { validateOpenProjects, @@ -288,6 +289,19 @@ describe('transformByQ', function () { }) }) + it(`WHEN getFilesRecursively on source code THEN ignores excluded directories`, async function () { + const sourceFolder = path.join(tempDir, 'src') + await fs.mkdir(sourceFolder) + await fs.writeFile(path.join(sourceFolder, 'HelloWorld.java'), 'sample content for the test file') + + const gitFolder = path.join(tempDir, '.git') + await fs.mkdir(gitFolder) + await fs.writeFile(path.join(gitFolder, 'config'), 'sample content for the test file') + + const zippedFiles = getFilesRecursively(tempDir, false) + assert.strictEqual(zippedFiles.length, 1) + }) + it(`WHEN getTableMapping on complete step 0 progressUpdates THEN map IDs to tables`, async function () { const stepZeroProgressUpdates = [ {