Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/amazonq/test/e2e/amazonq/transformByQ.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { qTestingFramework } from './framework/framework'
import sinon from 'sinon'
import { Messenger } from './framework/messenger'
import { JDKVersion, TransformationType, transformByQState } from 'aws-core-vscode/codewhisperer'
import { GumbyController, startTransformByQ, TabsStorage } from 'aws-core-vscode/amazonqGumby'
import { GumbyController, setMaven, startTransformByQ, TabsStorage } from 'aws-core-vscode/amazonqGumby'
import { using, registerAuthHook, TestFolder } from 'aws-core-vscode/test'
import { loginToIdC } from './utils/setup'
import { fs } from 'aws-core-vscode/shared'
Expand Down Expand Up @@ -338,7 +338,7 @@ describe('Amazon Q Code Transformation', function () {

it('WHEN transforming a Java 8 project E2E THEN job is successful', async function () {
transformByQState.setTransformationType(TransformationType.LANGUAGE_UPGRADE)
await startTransformByQ.setMaven()
await setMaven()
await startTransformByQ.processLanguageUpgradeTransformFormInput(tempDir, JDKVersion.JDK8, JDKVersion.JDK17)
await startTransformByQ.startTransformByQ()
assert.strictEqual(transformByQState.getPolledJobStatus(), 'COMPLETED')
Expand Down
12 changes: 7 additions & 5 deletions packages/core/src/amazonqGumby/chat/controller/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,14 @@ import {
compileProject,
finishHumanInTheLoop,
getValidLanguageUpgradeCandidateProjects,
openBuildLogFile,
openHilPomFile,
parseBuildFile,
postTransformationJob,
processLanguageUpgradeTransformFormInput,
processSQLConversionTransformFormInput,
startTransformByQ,
stopTransformByQ,
validateCanCompileProject,
setMaven,
getValidSQLConversionCandidateProjects,
validateSQLMetadataFile,
openHilPomFile,
} from '../../../codewhisperer/commands/startTransformByQ'
import { JDKVersion, TransformationCandidateProject, transformByQState } from '../../../codewhisperer/models/model'
import {
Expand Down Expand Up @@ -61,6 +57,12 @@ 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 {
setMaven,
openBuildLogFile,
parseBuildFile,
validateSQLMetadataFile,
} from '../../../codewhisperer/service/transformByQ/transformFileHandler'
import { getAuthType } from '../../../auth/utils'

// These events can be interactions within the chat,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/amazonqGumby/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export { default as MessengerUtils } from './chat/controller/messenger/messenger
export { GumbyController } from './chat/controller/controller'
export { TabsStorage } from '../amazonq/webview/ui/storages/tabsStorage'
export * as startTransformByQ from '../../src/codewhisperer/commands/startTransformByQ'
export { setMaven } from '../../src/codewhisperer/service/transformByQ/transformFileHandler'
export * from './errors'
104 changes: 1 addition & 103 deletions packages/core/src/codewhisperer/commands/startTransformByQ.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@

import * as vscode from 'vscode'
import * as fs from 'fs' // eslint-disable-line no-restricted-imports
import * as os from 'os'
import * as xml2js from 'xml2js'
import path from 'path'
import { getLogger } from '../../shared/logger'
import * as CodeWhispererConstants from '../models/constants'
Expand All @@ -18,7 +16,6 @@ import {
FolderInfo,
ZipManifest,
TransformByQStatus,
DB,
TransformationType,
TransformationCandidateProject,
} from '../models/model'
Expand Down Expand Up @@ -56,7 +53,6 @@ import { MetadataResult } from '../../shared/telemetry/telemetryClient'
import { submitFeedback } from '../../feedback/vue/submitFeedback'
import { placeholder } from '../../shared/vscode/commands2'
import {
AbsolutePathDetectedError,
AlternateDependencyVersionsNotFoundError,
JavaHomeNotSetError,
JobStartError,
Expand All @@ -71,6 +67,7 @@ import {
getJsonValuesFromManifestFile,
highlightPomIssueInProject,
parseVersionsListFromPomFile,
setMaven,
writeLogs,
} from '../service/transformByQ/transformFileHandler'
import { sleep } from '../../shared/utilities/timeoutUtils'
Expand All @@ -81,7 +78,6 @@ 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 { findStringInDirectory } from '../../shared/utilities/workspaceUtils'

function getFeedbackCommentData() {
Expand Down Expand Up @@ -111,63 +107,6 @@ export async function processSQLConversionTransformFormInput(pathToProject: stri
// targetJDKVersion defaults to JDK17, the only supported version, which is fine
}

export async function validateSQLMetadataFile(fileContents: string, message: any) {
try {
const sctData = await xml2js.parseStringPromise(fileContents)
const dbEntities = sctData['tree']['instances'][0]['ProjectModel'][0]['entities'][0]
const sourceDB = dbEntities['sources'][0]['DbServer'][0]['$']['vendor'].trim().toUpperCase()
const targetDB = dbEntities['targets'][0]['DbServer'][0]['$']['vendor'].trim().toUpperCase()
const sourceServerName = dbEntities['sources'][0]['DbServer'][0]['$']['name'].trim()
transformByQState.setSourceServerName(sourceServerName)
if (sourceDB !== DB.ORACLE) {
transformByQState.getChatMessenger()?.sendUnrecoverableErrorResponse('unsupported-source-db', message.tabID)
return false
} else if (targetDB !== DB.AURORA_POSTGRESQL && targetDB !== DB.RDS_POSTGRESQL) {
transformByQState.getChatMessenger()?.sendUnrecoverableErrorResponse('unsupported-target-db', message.tabID)
return false
}
transformByQState.setSourceDB(sourceDB)
transformByQState.setTargetDB(targetDB)

const serverNodeLocations =
sctData['tree']['instances'][0]['ProjectModel'][0]['relations'][0]['server-node-location']
const schemaNames = new Set<string>()
serverNodeLocations.forEach((serverNodeLocation: any) => {
const schemaNodes = serverNodeLocation['FullNameNodeInfoList'][0]['nameParts'][0][
'FullNameNodeInfo'
].filter((node: any) => node['$']['typeNode'].toLowerCase() === 'schema')
schemaNodes.forEach((node: any) => {
schemaNames.add(node['$']['nameNode'].toUpperCase())
})
})
transformByQState.setSchemaOptions(schemaNames) // user will choose one of these
getLogger().info(
`CodeTransformation: Parsed .sct file with source DB: ${sourceDB}, target DB: ${targetDB}, source host name: ${sourceServerName}, and schema names: ${Array.from(schemaNames)}`
)
} catch (err: any) {
getLogger().error('CodeTransformation: Error parsing .sct file. %O', err)
transformByQState.getChatMessenger()?.sendUnrecoverableErrorResponse('error-parsing-sct-file', message.tabID)
return false
}
return true
}

export async function setMaven() {
let mavenWrapperExecutableName = isWin() ? 'mvnw.cmd' : 'mvnw'
const mavenWrapperExecutablePath = path.join(transformByQState.getProjectPath(), mavenWrapperExecutableName)
if (fs.existsSync(mavenWrapperExecutablePath)) {
if (mavenWrapperExecutableName === 'mvnw') {
mavenWrapperExecutableName = './mvnw' // add the './' for non-Windows
} else if (mavenWrapperExecutableName === 'mvnw.cmd') {
mavenWrapperExecutableName = '.\\mvnw.cmd' // add the '.\' for Windows
}
transformByQState.setMavenName(mavenWrapperExecutableName)
} else {
transformByQState.setMavenName('mvn')
}
getLogger().info(`CodeTransformation: using Maven ${transformByQState.getMavenName()}`)
}

async function validateJavaHome(): Promise<boolean> {
const versionData = await getVersionData()
let javaVersionUsedByMaven = versionData[1]
Expand Down Expand Up @@ -290,41 +229,6 @@ export async function finalizeTransformByQ(status: string) {
}
}

export async function parseBuildFile() {
try {
const absolutePaths = ['users/', 'system/', 'volumes/', 'c:\\', 'd:\\']
const alias = path.basename(os.homedir())
absolutePaths.push(alias)
const buildFilePath = path.join(transformByQState.getProjectPath(), 'pom.xml')
if (fs.existsSync(buildFilePath)) {
const buildFileContents = fs.readFileSync(buildFilePath).toString().toLowerCase()
const detectedPaths = []
for (const absolutePath of absolutePaths) {
if (buildFileContents.includes(absolutePath)) {
detectedPaths.push(absolutePath)
}
}
if (detectedPaths.length > 0) {
const warningMessage = CodeWhispererConstants.absolutePathDetectedMessage(
detectedPaths.length,
path.basename(buildFilePath),
detectedPaths.join(', ')
)
transformByQState.getChatControllers()?.errorThrown.fire({
error: new AbsolutePathDetectedError(warningMessage),
tabID: ChatSessionManager.Instance.getSession().tabID,
})
getLogger().info('CodeTransformation: absolute path potentially in build file')
return warningMessage
}
}
} catch (err: any) {
// swallow error
getLogger().error(`CodeTransformation: error scanning for absolute paths, tranformation continuing: ${err}`)
}
return undefined
}

export async function preTransformationUploadCode() {
await vscode.commands.executeCommand('aws.amazonq.transformationHub.focus')

Expand Down Expand Up @@ -499,12 +403,6 @@ export async function openHilPomFile() {
)
}

export async function openBuildLogFile() {
const logFilePath = transformByQState.getPreBuildLogFilePath()
const doc = await vscode.workspace.openTextDocument(logFilePath)
await vscode.window.showTextDocument(doc)
}

export async function terminateHILEarly(jobID: string) {
// Call resume with "REJECTED" state which will put our service
// back into the normal flow and will not trigger HIL again for this step
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ import * as path from 'path'
import * as os from 'os'
import xml2js = require('xml2js')
import * as CodeWhispererConstants from '../../models/constants'
import { existsSync, writeFileSync } from 'fs' // eslint-disable-line no-restricted-imports
import { BuildSystem, FolderInfo, transformByQState } from '../../models/model'
import { existsSync, readFileSync, writeFileSync } from 'fs' // eslint-disable-line no-restricted-imports
import { BuildSystem, DB, FolderInfo, transformByQState } from '../../models/model'
import { IManifestFile } from '../../../amazonqFeatureDev/models'
import fs from '../../../shared/fs/fs'
import globals from '../../../shared/extensionGlobals'
import { ChatSessionManager } from '../../../amazonqGumby/chat/storages/chatSession'
import { AbsolutePathDetectedError } from '../../../amazonqGumby/errors'
import { getLogger } from '../../../shared/logger'
import { isWin } from '../../../shared/vscode/env'

export function getDependenciesFolderInfo(): FolderInfo {
const dependencyFolderName = `${CodeWhispererConstants.dependencyFolderName}${globals.clock.Date.now()}`
Expand All @@ -37,6 +41,104 @@ export async function checkBuildSystem(projectPath: string) {
return BuildSystem.Unknown
}

export async function parseBuildFile() {
try {
const absolutePaths = ['users/', 'system/', 'volumes/', 'c:\\', 'd:\\']
const alias = path.basename(os.homedir())
absolutePaths.push(alias)
const buildFilePath = path.join(transformByQState.getProjectPath(), 'pom.xml')
if (existsSync(buildFilePath)) {
const buildFileContents = readFileSync(buildFilePath).toString().toLowerCase()
const detectedPaths = []
for (const absolutePath of absolutePaths) {
if (buildFileContents.includes(absolutePath)) {
detectedPaths.push(absolutePath)
}
}
if (detectedPaths.length > 0) {
const warningMessage = CodeWhispererConstants.absolutePathDetectedMessage(
detectedPaths.length,
path.basename(buildFilePath),
detectedPaths.join(', ')
)
transformByQState.getChatControllers()?.errorThrown.fire({
error: new AbsolutePathDetectedError(warningMessage),
tabID: ChatSessionManager.Instance.getSession().tabID,
})
getLogger().info('CodeTransformation: absolute path potentially in build file')
return warningMessage
}
}
} catch (err: any) {
// swallow error
getLogger().error(`CodeTransformation: error scanning for absolute paths, tranformation continuing: ${err}`)
}
return undefined
}

export async function validateSQLMetadataFile(fileContents: string, message: any) {
try {
const sctData = await xml2js.parseStringPromise(fileContents)
const dbEntities = sctData['tree']['instances'][0]['ProjectModel'][0]['entities'][0]
const sourceDB = dbEntities['sources'][0]['DbServer'][0]['$']['vendor'].trim().toUpperCase()
const targetDB = dbEntities['targets'][0]['DbServer'][0]['$']['vendor'].trim().toUpperCase()
const sourceServerName = dbEntities['sources'][0]['DbServer'][0]['$']['name'].trim()
transformByQState.setSourceServerName(sourceServerName)
if (sourceDB !== DB.ORACLE) {
transformByQState.getChatMessenger()?.sendUnrecoverableErrorResponse('unsupported-source-db', message.tabID)
return false
} else if (targetDB !== DB.AURORA_POSTGRESQL && targetDB !== DB.RDS_POSTGRESQL) {
transformByQState.getChatMessenger()?.sendUnrecoverableErrorResponse('unsupported-target-db', message.tabID)
return false
}
transformByQState.setSourceDB(sourceDB)
transformByQState.setTargetDB(targetDB)

const serverNodeLocations =
sctData['tree']['instances'][0]['ProjectModel'][0]['relations'][0]['server-node-location']
const schemaNames = new Set<string>()
serverNodeLocations.forEach((serverNodeLocation: any) => {
const schemaNodes = serverNodeLocation['FullNameNodeInfoList'][0]['nameParts'][0][
'FullNameNodeInfo'
].filter((node: any) => node['$']['typeNode'].toLowerCase() === 'schema')
schemaNodes.forEach((node: any) => {
schemaNames.add(node['$']['nameNode'].toUpperCase())
})
})
transformByQState.setSchemaOptions(schemaNames) // user will choose one of these
getLogger().info(
`CodeTransformation: Parsed .sct file with source DB: ${sourceDB}, target DB: ${targetDB}, source host name: ${sourceServerName}, and schema names: ${Array.from(schemaNames)}`
)
} catch (err: any) {
getLogger().error('CodeTransformation: Error parsing .sct file. %O', err)
transformByQState.getChatMessenger()?.sendUnrecoverableErrorResponse('error-parsing-sct-file', message.tabID)
return false
}
return true
}

export async function setMaven() {
let mavenWrapperExecutableName = isWin() ? 'mvnw.cmd' : 'mvnw'
const mavenWrapperExecutablePath = path.join(transformByQState.getProjectPath(), mavenWrapperExecutableName)
if (existsSync(mavenWrapperExecutablePath)) {
if (mavenWrapperExecutableName === 'mvnw') {
mavenWrapperExecutableName = './mvnw' // add the './' for non-Windows
} else if (mavenWrapperExecutableName === 'mvnw.cmd') {
mavenWrapperExecutableName = '.\\mvnw.cmd' // add the '.\' for Windows
}
transformByQState.setMavenName(mavenWrapperExecutableName)
} else {
transformByQState.setMavenName('mvn')
}
getLogger().info(`CodeTransformation: using Maven ${transformByQState.getMavenName()}`)
}

export async function openBuildLogFile() {
const logFilePath = transformByQState.getPreBuildLogFilePath()
const doc = await vscode.workspace.openTextDocument(logFilePath)
await vscode.window.showTextDocument(doc)
}

export async function createPomCopy(
dirname: string,
pomFileVirtualFileReference: vscode.Uri,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@ import assert, { fail } from 'assert'
import * as vscode from 'vscode'
import * as sinon from 'sinon'
import { DB, transformByQState, TransformByQStoppedError } from '../../../codewhisperer/models/model'
import {
finalizeTransformationJob,
parseBuildFile,
setMaven,
stopTransformByQ,
validateSQLMetadataFile,
} from '../../../codewhisperer/commands/startTransformByQ'
import { stopTransformByQ, finalizeTransformationJob } from '../../../codewhisperer/commands/startTransformByQ'
import { HttpResponse } from 'aws-sdk'
import * as codeWhisperer from '../../../codewhisperer/client/codewhisperer'
import * as CodeWhispererConstants from '../../../codewhisperer/models/constants'
Expand Down Expand Up @@ -43,6 +37,11 @@ import { TransformationCandidateProject, ZipManifest } from '../../../codewhispe
import globals from '../../../shared/extensionGlobals'
import { env, fs } from '../../../shared'
import { convertDateToTimestamp, convertToTimeString } from '../../../shared/datetime'
import {
setMaven,
parseBuildFile,
validateSQLMetadataFile,
} from '../../../codewhisperer/service/transformByQ/transformFileHandler'

describe('transformByQ', function () {
let tempDir: string
Expand Down
Loading