Skip to content

Commit 52bd154

Browse files
author
David Hasani
committed
more dms
1 parent 0eac465 commit 52bd154

File tree

8 files changed

+362
-188
lines changed

8 files changed

+362
-188
lines changed

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

Lines changed: 22 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* the Gumby extension.
77
*/
88
import nodefs from 'fs' // eslint-disable-line no-restricted-imports
9-
import * as xml2js from 'xml2js'
109
import path from 'path'
1110
import * as vscode from 'vscode'
1211
import { GumbyNamedMessages, Messenger } from './messenger/messenger'
@@ -34,7 +33,7 @@ import {
3433
getValidSQLConversionCandidateProjects,
3534
validateSQLMetadataFile,
3635
} from '../../../codewhisperer/commands/startTransformByQ'
37-
import { DB, JDKVersion, transformByQState } from '../../../codewhisperer/models/model'
36+
import { JDKVersion, transformByQState } from '../../../codewhisperer/models/model'
3837
import {
3938
AbsolutePathDetectedError,
4039
AlternateDependencyVersionsNotFoundError,
@@ -263,7 +262,7 @@ export class GumbyController {
263262
const validProjects = await this.validateSQLConversionProjectsWithReplyOnError(message)
264263
if (validProjects.length > 0) {
265264
this.sessionStorage.getSession().updateCandidateProjects(validProjects)
266-
await this.messenger.sendSQLConversionProjectPrompt(validProjects, message.tabID)
265+
await this.messenger.sendSelectSQLMetadataFileMessage(message.tabID)
267266
}
268267
} catch (err: any) {
269268
getLogger().error(`Error handling SQL conversion: ${err}`)
@@ -442,24 +441,27 @@ export class GumbyController {
442441
private async handleUserSQLConversionProjectSelection(message: any) {
443442
await telemetry.codeTransform_submitSelection.run(async () => {
444443
const pathToProject: string = message.formSelectedValues['GumbyTransformSQLConversionProjectForm']
445-
const fromDB: DB = message.formSelectedValues['GumbyTransformDBFromForm']
446-
const toDB: DB = message.formSelectedValues['GumbyTransformDBToForm']
444+
const schema: string = message.formSelectedValues['GumbyTransformSQLSchemaForm']
447445

448446
telemetry.record({
449447
codeTransformProjectId: pathToProject === undefined ? telemetryUndefined : getStringHash(pathToProject),
450448
userChoice: 'Confirm-SQL-Conversion',
451449
})
452450

453451
const projectName = path.basename(pathToProject)
454-
this.messenger.sendSQLConversionProjectSelectionMessage(projectName, fromDB, toDB, message.tabID)
452+
this.messenger.sendSQLConversionProjectSelectionMessage(projectName, schema, message.tabID)
455453

456-
if (fromDB === DB.OTHER) {
457-
this.messenger.sendUnrecoverableErrorResponse('unsupported-source-db-version', message.tabID)
458-
return
459-
}
454+
await processSQLConversionTransformFormInput(pathToProject, schema)
460455

461-
await processSQLConversionTransformFormInput(pathToProject, fromDB, toDB)
462-
this.messenger.sendSelectSQLMetadataFileMessage(message.tabID)
456+
this.messenger.sendAsyncEventProgress(
457+
message.tabID,
458+
true,
459+
undefined,
460+
GumbyNamedMessages.JOB_SUBMISSION_STATUS_MESSAGE
461+
)
462+
this.messenger.sendJobSubmittedMessage(message.tabID)
463+
this.sessionStorage.getSession().conversationState = ConversationState.JOB_SUBMITTED
464+
await startTransformByQ()
463465
})
464466
}
465467

@@ -527,15 +529,15 @@ export class GumbyController {
527529

528530
private async processMetadataFile(message: any) {
529531
const fileUri = await vscode.window.showOpenDialog({
530-
canSelectMany: false, // Allow only one file to be selected
531-
openLabel: 'Select', // Label for the open button
532+
canSelectMany: false,
533+
openLabel: 'Select',
532534
filters: {
533535
'SCT metadata': ['sct'], // Restrict user to only pick a .sct file
534536
},
535537
})
536538

537539
if (!fileUri || fileUri.length === 0) {
538-
// User canceled the dialog
540+
// user closed the dialog
539541
this.transformationFinished({
540542
message: CodeWhispererConstants.jobCancelledChatMessage,
541543
tabID: message.tabID,
@@ -544,54 +546,19 @@ export class GumbyController {
544546
}
545547

546548
const fileContents = nodefs.readFileSync(fileUri[0].fsPath, 'utf-8')
547-
let parsedData: any = undefined
548-
xml2js.parseString(fileContents, (err, result) => {
549-
if (err) {
550-
getLogger().error('Error parsing SCT file:', err)
551-
} else {
552-
parsedData = result
553-
}
554-
})
555-
556-
if (!parsedData) {
557-
this.transformationFinished({
558-
message: CodeWhispererConstants.invalidMetadataFileUnknownIssueParsing,
559-
tabID: message.tabID,
560-
})
561-
return
562-
}
563549

564-
const isValidMetadata = await validateSQLMetadataFile(parsedData, message)
550+
const isValidMetadata = await validateSQLMetadataFile(fileContents, message)
565551
if (!isValidMetadata) {
566552
return
567553
}
568554

569-
// extract schema names from metadata file
570-
// probably if anything throws here we should just continue and not validate the user's schema input
571-
// unless we show a form with the schema options, in which case this needs to pass
572-
const serverNodeLocations =
573-
parsedData['tree']['instances'][0]['ProjectModel'][0]['relations'][0]['server-node-location']
574-
const schemaNames = new Set<string>()
575-
serverNodeLocations.forEach((serverNodeLocation: any) => {
576-
const schemaNodes = serverNodeLocation['FullNameNodeInfoList'][0]['nameParts'][0][
577-
'FullNameNodeInfo'
578-
].filter((node: any) => node['$']['typeNode'] === 'schema')
579-
schemaNodes.forEach((node: any) => {
580-
schemaNames.add(node['$']['nameNode'].toUpperCase())
581-
})
582-
})
583-
555+
this.messenger.sendSQLConversionMetadataReceivedMessage(message.tabID)
584556
transformByQState.setMetadataPathSQL(fileUri[0].fsPath)
585557

586-
this.messenger.sendAsyncEventProgress(
587-
message.tabID,
588-
true,
589-
undefined,
590-
GumbyNamedMessages.JOB_SUBMISSION_STATUS_MESSAGE
558+
await this.messenger.sendSQLConversionProjectPrompt(
559+
Array.from(this.sessionStorage.getSession().candidateProjects.values()),
560+
message.tabID
591561
)
592-
this.messenger.sendJobSubmittedMessage(message.tabID)
593-
this.sessionStorage.getSession().conversationState = ConversationState.JOB_SUBMITTED
594-
await startTransformByQ()
595562
}
596563

597564
private transformationFinished(data: { message: string | undefined; tabID: string }) {

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

Lines changed: 23 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,7 @@
99
*/
1010

1111
import { AuthFollowUpType, AuthMessageDataMap } from '../../../../amazonq/auth/model'
12-
import {
13-
DB,
14-
JDKVersion,
15-
TransformationCandidateProject,
16-
transformByQState,
17-
} from '../../../../codewhisperer/models/model'
12+
import { JDKVersion, TransformationCandidateProject, transformByQState } from '../../../../codewhisperer/models/model'
1813
import { FeatureAuthState } from '../../../../codewhisperer/util/authUtil'
1914
import * as CodeWhispererConstants from '../../../../codewhisperer/models/constants'
2015
import {
@@ -256,42 +251,18 @@ export class Messenger {
256251
type: 'select',
257252
title: 'Choose a project to transform',
258253
mandatory: true,
259-
260254
options: projectFormOptions,
261255
})
262256

263257
formItems.push({
264-
id: 'GumbyTransformDBFromForm',
258+
id: 'GumbyTransformSQLSchemaForm',
265259
type: 'select',
266-
title: 'Choose the source database',
260+
title: 'Choose the schema of the database',
267261
mandatory: true,
268-
options: [
269-
{
270-
value: DB.ORACLE,
271-
label: CodeWhispererConstants.oracleVendor,
272-
},
273-
{
274-
value: DB.OTHER,
275-
label: CodeWhispererConstants.otherVendor,
276-
},
277-
],
278-
})
279-
280-
formItems.push({
281-
id: 'GumbyTransformDBToForm',
282-
type: 'select',
283-
title: 'Choose the target database',
284-
mandatory: true,
285-
options: [
286-
{
287-
value: DB.RDS_POSTGRESQL,
288-
label: CodeWhispererConstants.rdsTargetVendor,
289-
},
290-
{
291-
value: DB.AURORA_POSTGRESQL,
292-
label: CodeWhispererConstants.auroraTargetVendor,
293-
},
294-
],
262+
options: Array.from(transformByQState.getSchemaOptions()).map((schema) => ({
263+
value: schema,
264+
label: schema,
265+
})),
295266
})
296267

297268
this.dispatcher.sendAsyncEventProgress(
@@ -558,14 +529,25 @@ export class Messenger {
558529
this.dispatcher.sendChatMessage(new ChatMessage({ message, messageType: 'prompt' }, tabID))
559530
}
560531

561-
public sendSQLConversionProjectSelectionMessage(projectName: string, fromDB: DB, toDB: DB, tabID: string) {
532+
public sendSQLConversionProjectSelectionMessage(projectName: string, schema: string, tabID: string) {
562533
const message = `### Transformation details
563534
-------------
564535
| | |
565536
| :------------------- | -------: |
566537
| **Project** | ${projectName} |
567-
| **Source DB** | ${fromDB} |
568-
| **Target DB** | ${toDB} |
538+
| **Schema** | ${schema} |
539+
`
540+
this.dispatcher.sendChatMessage(new ChatMessage({ message, messageType: 'prompt' }, tabID))
541+
}
542+
543+
public sendSQLConversionMetadataReceivedMessage(tabID: any) {
544+
const message = `### Transformation details
545+
-------------
546+
| | |
547+
| :------------------- | -------: |
548+
| **Source DB** | ${transformByQState.getSourceDB()} |
549+
| **Target DB** | ${transformByQState.getTargetDB()} |
550+
| **Host** | ${transformByQState.getSourceServerName()} |
569551
`
570552
this.dispatcher.sendChatMessage(new ChatMessage({ message, messageType: 'prompt' }, tabID))
571553
}
@@ -723,8 +705,8 @@ ${codeSnippet}
723705
)
724706
}
725707

726-
public sendSelectSQLMetadataFileMessage(tabID: string) {
727-
const message = 'Please select your metadata .sct file.'
708+
public async sendSelectSQLMetadataFileMessage(tabID: string) {
709+
const message = CodeWhispererConstants.selectSQLMetadataFileHelpMessage
728710
const buttons: ChatItemButton[] = []
729711

730712
buttons.push({

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

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import * as vscode from 'vscode'
77
import * as fs from 'fs' // eslint-disable-line no-restricted-imports
88
import * as os from 'os'
9+
import * as xml2js from 'xml2js'
910
import path from 'path'
1011
import { getLogger } from '../../shared/logger'
1112
import * as CodeWhispererConstants from '../models/constants'
@@ -96,11 +97,10 @@ export async function processLanguageUpgradeTransformFormInput(
9697
transformByQState.setTargetJDKVersion(toJDKVersion)
9798
}
9899

99-
export async function processSQLConversionTransformFormInput(pathToProject: string, fromDB: DB, toDB: DB) {
100+
export async function processSQLConversionTransformFormInput(pathToProject: string, schema: string) {
100101
transformByQState.setProjectName(path.basename(pathToProject))
101102
transformByQState.setProjectPath(pathToProject)
102-
transformByQState.setSourceDB(fromDB)
103-
transformByQState.setTargetDB(toDB)
103+
transformByQState.setSchema(schema)
104104
}
105105

106106
export async function setMaven() {
@@ -274,39 +274,69 @@ export async function parseBuildFile() {
274274
return undefined
275275
}
276276

277-
export async function validateSQLMetadataFile(sctData: any, message: any) {
277+
export async function validateSQLMetadataFile(fileContents: string, message: any) {
278278
try {
279+
let sctData: any = undefined
280+
xml2js.parseString(fileContents, (err, result) => {
281+
if (err) {
282+
getLogger().error('Error parsing .sct file, not valid XML:', err)
283+
throw err
284+
} else {
285+
sctData = result
286+
}
287+
})
288+
279289
const dbEntities = sctData['tree']['instances'][0]['ProjectModel'][0]['entities'][0]
280-
const sourceDB = dbEntities['sources'][0]['DbServer'][0]['$']['vendor']
281-
const targetDB = dbEntities['targets'][0]['DbServer'][0]['$']['vendor']
290+
const sourceDB = dbEntities['sources'][0]['DbServer'][0]['$']['vendor'].toUpperCase()
291+
const targetDB = dbEntities['targets'][0]['DbServer'][0]['$']['vendor'].toUpperCase()
282292
const sourceServerName = dbEntities['sources'][0]['DbServer'][0]['$']['name']
293+
if (!sourceServerName) {
294+
transformByQState.getChatControllers()?.transformationFinished.fire({
295+
message: CodeWhispererConstants.invalidMetadataFileNoSourceServerName,
296+
tabID: message.tabID,
297+
})
298+
return false
299+
}
283300
transformByQState.setSourceServerName(sourceServerName)
284-
if (sourceDB.toUpperCase() !== DB.ORACLE) {
301+
if (sourceDB !== DB.ORACLE) {
285302
transformByQState.getChatControllers()?.transformationFinished.fire({
286303
message: CodeWhispererConstants.invalidMetadataFileUnsupportedSourceVendor(sourceDB),
287304
tabID: message.tabID,
288305
})
289306
return false
290-
} else if (targetDB.toUpperCase() !== DB.AURORA_POSTGRESQL && targetDB.toUpperCase() !== DB.RDS_POSTGRESQL) {
307+
} else if (targetDB !== DB.AURORA_POSTGRESQL && targetDB !== DB.RDS_POSTGRESQL) {
291308
transformByQState.getChatControllers()?.transformationFinished.fire({
292309
message: CodeWhispererConstants.invalidMetadataFileUnsupportedTargetVendor(targetDB),
293310
tabID: message.tabID,
294311
})
295312
return false
296-
} else if (targetDB.toUpperCase() !== transformByQState.getTargetDB()) {
313+
}
314+
transformByQState.setSourceDB(sourceDB)
315+
transformByQState.setTargetDB(targetDB)
316+
317+
// extract schema names from metadata file
318+
const serverNodeLocations =
319+
sctData['tree']['instances'][0]['ProjectModel'][0]['relations'][0]['server-node-location']
320+
const schemaNames = new Set<string>()
321+
serverNodeLocations.forEach((serverNodeLocation: any) => {
322+
const schemaNodes = serverNodeLocation['FullNameNodeInfoList'][0]['nameParts'][0][
323+
'FullNameNodeInfo'
324+
].filter((node: any) => node['$']['typeNode'] === 'schema')
325+
schemaNodes.forEach((node: any) => {
326+
schemaNames.add(node['$']['nameNode'].toUpperCase())
327+
})
328+
})
329+
if (schemaNames.size === 0) {
297330
transformByQState.getChatControllers()?.transformationFinished.fire({
298-
message: CodeWhispererConstants.invalidMetadataFileTargetVendorMismatch(
299-
targetDB,
300-
transformByQState.getTargetDB()!
301-
),
331+
message: CodeWhispererConstants.invalidMetadataFileNoSchemaNamesFound,
302332
tabID: message.tabID,
303333
})
304334
return false
305335
}
306-
// TO-DO: when more than just Oracle is supported, validate that sourceDB === transformByQState.getSourceDB()
336+
transformByQState.setSchemaOptions(schemaNames) // user will choose one of these
307337
} catch (e: any) {
308338
transformByQState.getChatControllers()?.transformationFinished.fire({
309-
message: CodeWhispererConstants.invalidMetadataFileUnknownIssueParsing,
339+
message: `${CodeWhispererConstants.invalidMetadataFileUnknownIssueParsing} ${e.message ?? ''}`,
310340
tabID: message.tabID,
311341
})
312342
return false

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -504,20 +504,26 @@ export const unsupportedJavaVersionChatMessage = `Sorry, currently I can only up
504504

505505
export const unsupportedDatabaseChatMessage = `Sorry, currently I can only convert SQL built on Oracle. For more information, see the [Amazon Q documentation](${codeTransformPrereqDoc}).`
506506

507-
export const invalidMetadataFileNoRulesJson =
508-
"Sorry, your metadata ZIP appears to be invalid; it does not contain an 'sct-rules.json' file."
507+
export const selectSQLMetadataFileHelpMessage =
508+
'You can download the .sct file by going to AWS Console -> AWS DMS -> Migration Projects. Open the schema conversion project and navigate to the S3 bucket linked to it. You will find the ZIP containing the .sct file under the <schema-conversion-project>/ directory.'
509+
510+
export const invalidMetadataFileNoSourceServerName =
511+
'Sorry, your metadata .sct file appears to be invalid; I could not find the server name of the source DB.'
509512

510513
export const invalidMetadataFileUnsupportedSourceVendor = (vendor: string) =>
511-
`Sorry, your metadata file appears to be invalid; the source vendor is '${vendor}', but only Oracle is supported.`
514+
`Sorry, your metadata .sct file appears to be invalid; the source vendor is '${vendor}', but only Oracle is supported.`
512515

513516
export const invalidMetadataFileUnsupportedTargetVendor = (vendor: string) =>
514-
`Sorry, your metadata file appears to be invalid; the target vendor is '${vendor}', but only Amazon Aurora PostgreSQL and Amazon RDS for PostgreSQL are supported.`
517+
`Sorry, your metadata .sct file appears to be invalid; the target vendor is '${vendor}', but only Amazon Aurora PostgreSQL and Amazon RDS for PostgreSQL are supported.`
515518

516519
export const invalidMetadataFileTargetVendorMismatch = (foundVendor: string, selectedVendor: string) =>
517-
`Sorry, your metadata file appears to be invalid; the target vendor is '${foundVendor}', but you previously selected a target vendor of '${selectedVendor}'.`
520+
`Sorry, your metadata .sct file appears to be invalid; the target vendor is '${foundVendor}', but you previously selected a target vendor of '${selectedVendor}'.`
521+
522+
export const invalidMetadataFileNoSchemaNamesFound =
523+
'Sorry, your metadata.sct file appears to be invalid; I could not find any schema names.'
518524

519525
export const invalidMetadataFileUnknownIssueParsing =
520-
'Sorry, I had an issue parsing the metadata ZIP you provided; please make sure it is valid.'
526+
'Sorry, I had an issue parsing the metadata .sct file you provided; please make sure it is valid.'
521527

522528
export const oracleVendor = 'Oracle'
523529

0 commit comments

Comments
 (0)