Skip to content

Commit

Permalink
feat(amazonq): enable SQL conversions feature (#5925)
Browse files Browse the repository at this point in the history
## Problem

Enable the SQL conversion feature for `/transform`.

## Solution

Enable feature, intended for 11/21 release.


---

<!--- REMINDER: Ensure that your PR meets the guidelines in
CONTRIBUTING.md -->

License: I confirm that my contribution is made under the terms of the
Apache 2.0 license.

---------

Co-authored-by: David Hasani <[email protected]>
  • Loading branch information
dhasani23 and David Hasani authored Nov 18, 2024
1 parent 988dea0 commit 87ea4d9
Show file tree
Hide file tree
Showing 14 changed files with 177 additions and 85 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type": "Feature",
"description": "Feature(Amazon Q Code Transformation): support conversions of embedded SQL from Oracle to PostgreSQL"
}
11 changes: 1 addition & 10 deletions packages/core/src/amazonq/webview/ui/tabs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
import { QuickActionCommandGroup } from '@aws/mynah-ui'

Expand Down Expand Up @@ -47,14 +46,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!`,
welcome: 'Welcome to Code Transformation!',
},
}
47 changes: 35 additions & 12 deletions packages/core/src/amazonqGumby/chat/controller/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,15 +256,15 @@ export class Messenger {
formItems.push({
id: 'GumbyTransformSQLConversionProjectForm',
type: 'select',
title: 'Choose a project to transform',
title: CodeWhispererConstants.chooseProjectFormTitle,
mandatory: true,
options: projectFormOptions,
})

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,
Expand All @@ -275,7 +275,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,
})
)

Expand Down Expand Up @@ -394,7 +394,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
}

Expand Down Expand Up @@ -426,6 +426,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':
Expand All @@ -451,23 +452,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)
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/amazonqGumby/models/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
43 changes: 39 additions & 4 deletions packages/core/src/codewhisperer/commands/startTransformByQ.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
TransformByQStatus,
DB,
TransformationType,
TransformationCandidateProject,
} from '../models/model'
import {
createZipManifest,
Expand Down Expand Up @@ -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 { findStringInDirectory } from '../../shared/utilities/workspaceUtils'

function getFeedbackCommentData() {
const jobId = transformByQState.getJobId()
Expand Down Expand Up @@ -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') {
Expand Down Expand Up @@ -730,13 +733,45 @@ 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 resultLog = ''
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:@', 'jdbc:odbc:']
for (const str of searchStrings) {
const spawnResult = await findStringInDirectory(str, project.path)
// just for telemetry purposes
if (spawnResult.error || spawnResult.stderr) {
resultLog += `search failed: ${JSON.stringify(spawnResult)}`
} else {
resultLog += `search succeeded: ${spawnResult.exitCode}`
}
getLogger().info(`CodeTransformation: searching for ${str} in ${project.path}, result = ${resultLog}`)
if (spawnResult.exitCode === 0) {
embeddedSQLProjects.push(project)
break
}
}
}
getLogger().info(
`CodeTransformation: found ${embeddedSQLProjects.length} projects with embedded SQL statements`
)
telemetry.record({
codeTransformMetadata: resultLog,
})
})
return embeddedSQLProjects
}

export async function setTransformationToRunningState() {
Expand Down
29 changes: 17 additions & 12 deletions packages/core/src/codewhisperer/models/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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'
Expand All @@ -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.'
Expand All @@ -507,7 +512,7 @@ export const absolutePathDetectedMessage = (numPaths: number, buildFile: string,
export const unsupportedJavaVersionChatMessage = `I can only upgrade Java 8, Java 11, or Java 17 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.'
Expand Down Expand Up @@ -578,19 +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 = `I couldn\'t find a project that I can upgrade. Your Java project must be built on Maven and contain a pom.xml file. For more information, see the [Amazon Q documentation](${codeTransformPrereqDoc}).`
export const noPomXmlFoundChatMessage = `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 upgrade Java 8 or Java 11 projects built on Maven, or Oracle SQL to PostgreSQL statements in Java projects. For more information, see the [Amazon Q documentation](${codeTransformPrereqDoc}).`

export const noPomXmlFoundNotification = `None of your open modules are supported for code transformation with Amazon Q. A pom.xml is required for transformation.`

Expand Down Expand Up @@ -677,6 +678,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 =
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/codewhisperer/models/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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?: {
Expand Down
Loading

0 comments on commit 87ea4d9

Please sign in to comment.