Skip to content

Commit 87ea4d9

Browse files
dhasani23David Hasani
andauthored
feat(amazonq): enable SQL conversions feature (#5925)
## 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]>
1 parent 988dea0 commit 87ea4d9

File tree

14 files changed

+177
-85
lines changed

14 files changed

+177
-85
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "Feature(Amazon Q Code Transformation): support conversions of embedded SQL from Oracle to PostgreSQL"
4+
}

packages/core/src/amazonq/webview/ui/tabs/constants.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
5-
import { isSQLTransformReady } from '../../../../dev/config'
65
import { TabType } from '../storages/tabsStorage'
76
import { QuickActionCommandGroup } from '@aws/mynah-ui'
87

@@ -47,14 +46,6 @@ What would you like to work on?`,
4746
gumby: {
4847
title: 'Q - Code Transformation',
4948
placeholder: 'Open a new tab to chat with Q',
50-
welcome: isSQLTransformReady
51-
? `Welcome to code transformation!
52-
53-
I can help you with the following tasks:
54-
- Upgrade your Java 8 and Java 11 codebases to Java 17
55-
- Convert embedded SQL from Oracle databases to PostgreSQL
56-
57-
What would you like to do? You can enter 'language upgrade' or 'SQL conversion'.`
58-
: `Welcome to code transformation!`,
49+
welcome: 'Welcome to Code Transformation!',
5950
},
6051
}

packages/core/src/amazonqGumby/chat/controller/controller.ts

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
getValidSQLConversionCandidateProjects,
3434
validateSQLMetadataFile,
3535
} from '../../../codewhisperer/commands/startTransformByQ'
36-
import { JDKVersion, transformByQState } from '../../../codewhisperer/models/model'
36+
import { JDKVersion, TransformationCandidateProject, transformByQState } from '../../../codewhisperer/models/model'
3737
import {
3838
AbsolutePathDetectedError,
3939
AlternateDependencyVersionsNotFoundError,
@@ -62,7 +62,6 @@ import { getStringHash } from '../../../shared/utilities/textUtilities'
6262
import { getVersionData } from '../../../codewhisperer/service/transformByQ/transformMavenHandler'
6363
import AdmZip from 'adm-zip'
6464
import { AuthError } from '../../../auth/sso/server'
65-
import { isSQLTransformReady } from '../../../dev/config'
6665

6766
// These events can be interactions within the chat,
6867
// or elsewhere in the IDE
@@ -190,12 +189,31 @@ export class GumbyController {
190189
}
191190

192191
private async transformInitiated(message: any) {
193-
// feature flag for SQL transformations
194-
if (!isSQLTransformReady) {
192+
// silently check for projects eligible for SQL conversion
193+
let embeddedSQLProjects: TransformationCandidateProject[] = []
194+
try {
195+
embeddedSQLProjects = await getValidSQLConversionCandidateProjects()
196+
} catch (err) {
197+
getLogger().error(`CodeTransformation: error validating SQL conversion projects: ${err}`)
198+
}
199+
200+
if (embeddedSQLProjects.length === 0) {
195201
await this.handleLanguageUpgrade(message)
196202
return
197203
}
198204

205+
let javaUpgradeProjects: TransformationCandidateProject[] = []
206+
try {
207+
javaUpgradeProjects = await getValidLanguageUpgradeCandidateProjects()
208+
} catch (err) {
209+
getLogger().error(`CodeTransformation: error validating Java upgrade projects: ${err}`)
210+
}
211+
212+
if (javaUpgradeProjects.length === 0) {
213+
await this.handleSQLConversion(message)
214+
return
215+
}
216+
199217
// if previous transformation was already running, show correct message to user
200218
switch (this.sessionStorage.getSession().conversationState) {
201219
case ConversationState.JOB_SUBMITTED:
@@ -224,7 +242,10 @@ export class GumbyController {
224242
this.sessionStorage.getSession().conversationState = ConversationState.WAITING_FOR_TRANSFORMATION_OBJECTIVE
225243
this.messenger.sendStaticTextResponse('choose-transformation-objective', message.tabID)
226244
this.messenger.sendChatInputEnabled(message.tabID, true)
227-
this.messenger.sendUpdatePlaceholder(message.tabID, "Enter 'language upgrade' or 'SQL conversion'")
245+
this.messenger.sendUpdatePlaceholder(
246+
message.tabID,
247+
CodeWhispererConstants.chooseTransformationObjectivePlaceholder
248+
)
228249
}
229250

230251
private async beginTransformation(message: any) {
@@ -310,13 +331,7 @@ export class GumbyController {
310331

311332
private async validateSQLConversionProjects(message: any) {
312333
try {
313-
const validProjects = await telemetry.codeTransform_validateProject.run(async () => {
314-
telemetry.record({
315-
codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId(),
316-
})
317-
const validProjects = await getValidSQLConversionCandidateProjects()
318-
return validProjects
319-
})
334+
const validProjects = await getValidSQLConversionCandidateProjects()
320335
return validProjects
321336
} catch (e: any) {
322337
if (e instanceof NoJavaProjectsFoundError) {
@@ -624,6 +639,14 @@ export class GumbyController {
624639

625640
case ConversationState.WAITING_FOR_TRANSFORMATION_OBJECTIVE: {
626641
const objective = data.message.trim().toLowerCase()
642+
// since we're prompting the user, their project(s) must be eligible for both types of transformations, so track how often this happens here
643+
if (objective === 'language upgrade' || objective === 'sql conversion') {
644+
telemetry.codeTransform_submitSelection.emit({
645+
codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId(),
646+
userChoice: objective,
647+
result: 'Succeeded',
648+
})
649+
}
627650
if (objective === 'language upgrade') {
628651
await this.handleLanguageUpgrade(data)
629652
} else if (objective === 'sql conversion') {

packages/core/src/amazonqGumby/chat/controller/messenger/messenger.ts

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -256,15 +256,15 @@ export class Messenger {
256256
formItems.push({
257257
id: 'GumbyTransformSQLConversionProjectForm',
258258
type: 'select',
259-
title: 'Choose a project to transform',
259+
title: CodeWhispererConstants.chooseProjectFormTitle,
260260
mandatory: true,
261261
options: projectFormOptions,
262262
})
263263

264264
formItems.push({
265265
id: 'GumbyTransformSQLSchemaForm',
266266
type: 'select',
267-
title: 'Choose the schema of the database',
267+
title: CodeWhispererConstants.chooseSchemaFormTitle,
268268
mandatory: true,
269269
options: Array.from(transformByQState.getSchemaOptions()).map((schema) => ({
270270
value: schema,
@@ -275,7 +275,7 @@ export class Messenger {
275275
this.dispatcher.sendAsyncEventProgress(
276276
new AsyncEventProgressMessage(tabID, {
277277
inProgress: true,
278-
message: 'I can convert your embedded SQL, but I need some more info from you first.',
278+
message: CodeWhispererConstants.chooseProjectSchemaFormMessage,
279279
})
280280
)
281281

@@ -394,7 +394,7 @@ export class Messenger {
394394
message = 'I will continue transforming your code without upgrading this dependency.'
395395
break
396396
case 'choose-transformation-objective':
397-
message = 'Choose your transformation objective.'
397+
message = CodeWhispererConstants.chooseTransformationObjective
398398
break
399399
}
400400

@@ -426,6 +426,7 @@ export class Messenger {
426426
message = CodeWhispererConstants.noJavaProjectsFoundChatMessage
427427
break
428428
case 'no-maven-java-project-found':
429+
// shown when user has no pom.xml, but at this point also means they have no eligible SQL conversion projects
429430
message = CodeWhispererConstants.noPomXmlFoundChatMessage
430431
break
431432
case 'could-not-compile-project':
@@ -451,23 +452,7 @@ export class Messenger {
451452
break
452453
}
453454

454-
const buttons: ChatItemButton[] = []
455-
buttons.push({
456-
keepCardAfterClick: false,
457-
text: CodeWhispererConstants.startTransformationButtonText,
458-
id: ButtonActions.CONFIRM_START_TRANSFORMATION_FLOW,
459-
})
460-
461-
this.dispatcher.sendChatMessage(
462-
new ChatMessage(
463-
{
464-
message,
465-
messageType: 'ai-prompt',
466-
buttons,
467-
},
468-
tabID
469-
)
470-
)
455+
this.sendJobFinishedMessage(tabID, message)
471456
}
472457

473458
/**

packages/core/src/amazonqGumby/models/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
export const gumbyChat = 'gumbyChat'
88

99
// This sets the tab name
10-
export const featureName = 'Q - Code Transform'
10+
export const featureName = 'Q - Code Transformation'
1111

1212
export const dependencyNoAvailableVersions = 'no available versions'

packages/core/src/codewhisperer/commands/startTransformByQ.ts

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
TransformByQStatus,
2121
DB,
2222
TransformationType,
23+
TransformationCandidateProject,
2324
} from '../models/model'
2425
import {
2526
createZipManifest,
@@ -80,6 +81,8 @@ import { setContext } from '../../shared/vscode/setContext'
8081
import { makeTemporaryToolkitFolder } from '../../shared'
8182
import globals from '../../shared/extensionGlobals'
8283
import { convertDateToTimestamp } from '../../shared/datetime'
84+
import { isWin } from '../../shared/vscode/env'
85+
import { findStringInDirectory } from '../../shared/utilities/workspaceUtils'
8386

8487
function getFeedbackCommentData() {
8588
const jobId = transformByQState.getJobId()
@@ -150,7 +153,7 @@ export async function validateSQLMetadataFile(fileContents: string, message: any
150153
}
151154

152155
export async function setMaven() {
153-
let mavenWrapperExecutableName = os.platform() === 'win32' ? 'mvnw.cmd' : 'mvnw'
156+
let mavenWrapperExecutableName = isWin() ? 'mvnw.cmd' : 'mvnw'
154157
const mavenWrapperExecutablePath = path.join(transformByQState.getProjectPath(), mavenWrapperExecutableName)
155158
if (fs.existsSync(mavenWrapperExecutablePath)) {
156159
if (mavenWrapperExecutableName === 'mvnw') {
@@ -730,13 +733,45 @@ export async function finalizeTransformationJob(status: string) {
730733
export async function getValidLanguageUpgradeCandidateProjects() {
731734
const openProjects = await getOpenProjects()
732735
const javaMavenProjects = await validateOpenProjects(openProjects)
736+
getLogger().info(`CodeTransformation: found ${javaMavenProjects.length} projects eligible for language upgrade`)
733737
return javaMavenProjects
734738
}
735739

736740
export async function getValidSQLConversionCandidateProjects() {
737-
const openProjects = await getOpenProjects()
738-
const javaProjects = await getJavaProjects(openProjects)
739-
return javaProjects
741+
const embeddedSQLProjects: TransformationCandidateProject[] = []
742+
await telemetry.codeTransform_validateProject.run(async () => {
743+
telemetry.record({
744+
codeTransformSessionId: CodeTransformTelemetryState.instance.getSessionId(),
745+
})
746+
const openProjects = await getOpenProjects()
747+
const javaProjects = await getJavaProjects(openProjects)
748+
let resultLog = ''
749+
for (const project of javaProjects) {
750+
// as long as at least one of these strings is found, project contains embedded SQL statements
751+
const searchStrings = ['oracle.jdbc.OracleDriver', 'jdbc:oracle:thin:@', 'jdbc:oracle:oci:@', 'jdbc:odbc:']
752+
for (const str of searchStrings) {
753+
const spawnResult = await findStringInDirectory(str, project.path)
754+
// just for telemetry purposes
755+
if (spawnResult.error || spawnResult.stderr) {
756+
resultLog += `search failed: ${JSON.stringify(spawnResult)}`
757+
} else {
758+
resultLog += `search succeeded: ${spawnResult.exitCode}`
759+
}
760+
getLogger().info(`CodeTransformation: searching for ${str} in ${project.path}, result = ${resultLog}`)
761+
if (spawnResult.exitCode === 0) {
762+
embeddedSQLProjects.push(project)
763+
break
764+
}
765+
}
766+
}
767+
getLogger().info(
768+
`CodeTransformation: found ${embeddedSQLProjects.length} projects with embedded SQL statements`
769+
)
770+
telemetry.record({
771+
codeTransformMetadata: resultLog,
772+
})
773+
})
774+
return embeddedSQLProjects
740775
}
741776

742777
export async function setTransformationToRunningState() {

packages/core/src/codewhisperer/models/constants.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,10 @@ export const codeTransformLocThreshold = 100000
450450
export const jobStartedChatMessage =
451451
'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.'
452452

453+
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".`
454+
455+
export const chooseTransformationObjectivePlaceholder = 'Enter "language upgrade" or "sql conversion"'
456+
453457
export const uploadingCodeStepMessage = 'Upload your code'
454458

455459
export const buildCodeStepMessage = 'Build uploaded code in secure build environment'
@@ -477,6 +481,8 @@ export const failedStepMessage = 'The step failed, fetching additional details..
477481

478482
export const jobCompletedMessage = 'The transformation completed.'
479483

484+
export const noChangesMadeMessage = "I didn't make any changes for this transformation."
485+
480486
export const noOngoingJobMessage = 'No ongoing job.'
481487

482488
export const nothingToShowMessage = 'Nothing to show'
@@ -490,8 +496,7 @@ export const startTransformationButtonText = 'Start a new transformation'
490496

491497
export const stopTransformationButtonText = 'Stop transformation'
492498

493-
export const checkingForProjectsChatMessage =
494-
'I am checking for open projects that are eligible for Code Transformation.'
499+
export const checkingForProjectsChatMessage = 'Checking for eligible projects...'
495500

496501
export const buildStartedChatMessage =
497502
'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,
507512
export const unsupportedJavaVersionChatMessage = `I can only upgrade Java 8, Java 11, or Java 17 projects. For more information, see the [Amazon Q documentation](${codeTransformPrereqDoc}).`
508513

509514
export const selectSQLMetadataFileHelpMessage =
510-
'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.'
515+
'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).'
511516

512517
export const invalidMetadataFileUnsupportedSourceDB =
513518
'I can only convert SQL for migrations from an Oracle source database. The provided .sct file indicates another source database for this migration.'
@@ -578,19 +583,15 @@ export const jobCancelledChatMessage =
578583

579584
export const jobCancelledNotification = 'You cancelled the transformation.'
580585

581-
export const jobCompletedChatMessage =
582-
'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.'
586+
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.`
583587

584-
export const jobCompletedNotification =
585-
'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.'
588+
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.`
586589

587-
export const jobPartiallyCompletedChatMessage =
588-
'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.'
590+
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.`
589591

590-
export const jobPartiallyCompletedNotification =
591-
'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.'
592+
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.`
592593

593-
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}).`
594+
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}).`
594595

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

@@ -677,6 +678,10 @@ export const chooseSourceVersionFormTitle = 'Choose the source code version'
677678

678679
export const chooseTargetVersionFormTitle = 'Choose the target code version'
679680

681+
export const chooseSchemaFormTitle = 'Choose the schema of the database'
682+
683+
export const chooseProjectSchemaFormMessage = 'To continue, choose the project and schema for this transformation.'
684+
680685
export const skipUnitTestsFormTitle = 'Choose to skip unit tests'
681686

682687
export const skipUnitTestsFormMessage =

packages/core/src/codewhisperer/models/model.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ export class ZipManifest {
328328
buildLogs: string = 'build-logs.txt'
329329
version: string = '1.0'
330330
hilCapabilities: string[] = ['HIL_1pDependency_VersionUpgrade']
331-
transformCapabilities: string[] = ['EXPLAINABILITY_V1'] // TO-DO: for SQL conversions, maybe make this = []
331+
transformCapabilities: string[] = ['EXPLAINABILITY_V1']
332332
customBuildCommand: string = 'clean test'
333333
requestedConversions?: {
334334
sqlConversion?: {

0 commit comments

Comments
 (0)