Skip to content

Commit 67a2d6b

Browse files
committed
initial work
1 parent c529794 commit 67a2d6b

File tree

33 files changed

+208
-212
lines changed

33 files changed

+208
-212
lines changed

packages/amazonq/test/unit/codewhisperer/service/securityScanHandler.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
import assert from 'assert'
1818
import sinon from 'sinon'
1919
import * as vscode from 'vscode'
20-
import fs from 'fs'
20+
import * as fs from 'fs'
2121

2222
const mockCodeScanFindings = JSON.stringify([
2323
{
@@ -72,7 +72,7 @@ describe('securityScanHandler', function () {
7272
let mockClient: Stub<DefaultCodeWhispererClient>
7373
beforeEach(function () {
7474
mockClient = stub(DefaultCodeWhispererClient)
75-
sinon.stub(fs, 'existsSync').returns(true)
75+
sinon.stub(fs, 'existsSync').resolves(true)
7676
sinon.stub(fs, 'statSync').returns({ isFile: () => true } as fs.Stats)
7777
})
7878

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
* This class is responsible for responding to UI events by calling
66
* the Gumby extension.
77
*/
8-
import nodefs from 'fs'
98
import path from 'path'
109
import * as vscode from 'vscode'
1110
import { GumbyNamedMessages, Messenger } from './messenger/messenger'
@@ -56,6 +55,7 @@ import { getAuthType } from '../../../codewhisperer/service/transformByQ/transfo
5655
import DependencyVersions from '../../models/dependencies'
5756
import { getStringHash } from '../../../shared/utilities/textUtilities'
5857
import { getVersionData } from '../../../codewhisperer/service/transformByQ/transformMavenHandler'
58+
import { fs } from '../../../shared'
5959

6060
// These events can be interactions within the chat,
6161
// or elsewhere in the IDE
@@ -488,7 +488,7 @@ export class GumbyController {
488488
const session = this.sessionStorage.getSession()
489489
switch (session.conversationState) {
490490
case ConversationState.PROMPT_JAVA_HOME: {
491-
const pathToJavaHome = extractPath(data.message)
491+
const pathToJavaHome = await extractPath(data.message)
492492

493493
if (pathToJavaHome) {
494494
await this.prepareProjectForSubmission({
@@ -562,7 +562,7 @@ export class GumbyController {
562562
* @param text
563563
* @returns the absolute path if path points to existing folder, otherwise undefined
564564
*/
565-
function extractPath(text: string): string | undefined {
565+
async function extractPath(text: string): Promise<string | undefined> {
566566
const resolvedPath = path.resolve(text.trim())
567-
return nodefs.existsSync(resolvedPath) ? resolvedPath : undefined
567+
return (await fs.exists(resolvedPath)) ? resolvedPath : undefined
568568
}

packages/core/src/awsService/accessanalyzer/vue/iamPolicyChecks.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*/
55

66
import * as vscode from 'vscode'
7-
import * as fs from 'fs'
87
import * as path from 'path'
98
import { getLogger, Logger } from '../../../shared/logger'
109
import { localize } from '../../../shared/utilities/vsCodeUtils'
@@ -15,7 +14,7 @@ import { AccessAnalyzer, SharedIniFileCredentials } from 'aws-sdk'
1514
import { execFileSync } from 'child_process'
1615
import { ToolkitError } from '../../../shared/errors'
1716
import { makeTemporaryToolkitFolder, tryRemoveFolder } from '../../../shared/filesystemUtilities'
18-
import { globals } from '../../../shared'
17+
import { fs, globals } from '../../../shared'
1918
import {
2019
IamPolicyChecksConstants,
2120
PolicyChecksCheckType,
@@ -331,7 +330,7 @@ export class IamPolicyChecksWebview extends VueWebview {
331330
const document = IamPolicyChecksWebview.editedDocumentFileName
332331
customPolicyCheckDiagnosticCollection.clear()
333332
if (referenceDocument !== '') {
334-
fs.writeFileSync(tempFilePath, referenceDocument)
333+
await fs.writeFile(tempFilePath, referenceDocument)
335334
} else {
336335
this.onCustomPolicyCheckResponse.fire([
337336
IamPolicyChecksConstants.MissingReferenceDocError,
@@ -807,8 +806,8 @@ export async function renderIamPolicyChecks(context: ExtContext): Promise<VueWeb
807806

808807
// Helper function to get document contents from a path
809808
export async function _readCustomChecksFile(input: string): Promise<string> {
810-
if (fs.existsSync(input)) {
811-
return fs.readFileSync(input).toString()
809+
if (await fs.exists(input)) {
810+
return (await fs.readFileBytes(input)).toString()
812811
} else {
813812
try {
814813
const [region, bucket, key] = parseS3Uri(input)

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

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*/
55

66
import * as vscode from 'vscode'
7-
import * as fs from 'fs'
87
import * as os from 'os'
98
import path from 'path'
109
import { getLogger } from '../../shared/logger'
@@ -72,7 +71,7 @@ import DependencyVersions from '../../amazonqGumby/models/dependencies'
7271
import { dependencyNoAvailableVersions } from '../../amazonqGumby/models/constants'
7372
import { HumanInTheLoopManager } from '../service/transformByQ/humanInTheLoopManager'
7473
import { setContext } from '../../shared/vscode/setContext'
75-
import { makeTemporaryToolkitFolder } from '../../shared'
74+
import { fs, makeTemporaryToolkitFolder } from '../../shared'
7675
import globals from '../../shared/extensionGlobals'
7776

7877
function getFeedbackCommentData() {
@@ -95,7 +94,7 @@ export async function processTransformFormInput(
9594
export async function setMaven() {
9695
let mavenWrapperExecutableName = os.platform() === 'win32' ? 'mvnw.cmd' : 'mvnw'
9796
const mavenWrapperExecutablePath = path.join(transformByQState.getProjectPath(), mavenWrapperExecutableName)
98-
if (fs.existsSync(mavenWrapperExecutablePath)) {
97+
if (await fs.exists(mavenWrapperExecutablePath)) {
9998
if (mavenWrapperExecutableName === 'mvnw') {
10099
mavenWrapperExecutableName = './mvnw' // add the './' for non-Windows
101100
} else if (mavenWrapperExecutableName === 'mvnw.cmd') {
@@ -234,8 +233,8 @@ export async function parseBuildFile() {
234233
const alias = path.basename(os.homedir())
235234
absolutePaths.push(alias)
236235
const buildFilePath = path.join(transformByQState.getProjectPath(), 'pom.xml')
237-
if (fs.existsSync(buildFilePath)) {
238-
const buildFileContents = fs.readFileSync(buildFilePath).toString().toLowerCase()
236+
if (await fs.exists(buildFilePath)) {
237+
const buildFileContents = (await fs.readFileBytes(buildFilePath)).toString().toLowerCase()
239238
const detectedPaths = []
240239
for (const absolutePath of absolutePaths) {
241240
if (buildFileContents.includes(absolutePath)) {
@@ -599,7 +598,7 @@ export async function pollTransformationStatusUntilPlanReady(jobId: string) {
599598
throw e
600599
}
601600

602-
if (fs.existsSync(pathToLog) && !transformByQState.isCancelled()) {
601+
if ((await fs.exists(pathToLog)) && !transformByQState.isCancelled()) {
603602
throw new TransformationPreBuildError()
604603
} else {
605604
// not strictly needed to reset path here and above; doing it just to represent unavailable logs
@@ -620,7 +619,7 @@ export async function pollTransformationStatusUntilPlanReady(jobId: string) {
620619

621620
if (plan !== undefined) {
622621
const planFilePath = path.join(transformByQState.getProjectPath(), 'transformation-plan.md')
623-
fs.writeFileSync(planFilePath, plan)
622+
await fs.writeFile(planFilePath, plan)
624623
await vscode.commands.executeCommand('markdown.showPreview', vscode.Uri.file(planFilePath))
625624
transformByQState.setPlanFilePath(planFilePath)
626625
await setContext('gumby.isPlanAvailable', true)
@@ -752,7 +751,7 @@ export async function postTransformationJob() {
752751
}
753752

754753
if (transformByQState.getPayloadFilePath() !== '') {
755-
fs.rmSync(transformByQState.getPayloadFilePath(), { recursive: true, force: true }) // delete ZIP if it exists
754+
await fs.delete(transformByQState.getPayloadFilePath(), { recursive: true, force: true }) // delete ZIP if it exists
756755
}
757756
}
758757

packages/core/src/codewhisperer/service/securityScanHandler.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
import { sleep } from '../../shared/utilities/timeoutUtils'
1717
import * as codewhispererClient from '../client/codewhisperer'
1818
import * as CodeWhispererConstants from '../models/constants'
19-
import { existsSync, statSync, readFileSync } from 'fs'
19+
import { existsSync, statSync } from 'fs'
2020
import { RawCodeScanIssue } from '../models/model'
2121
import * as crypto from 'crypto'
2222
import path = require('path')
@@ -39,6 +39,7 @@ import {
3939
UploadArtifactToS3Error,
4040
} from '../models/errors'
4141
import { getTelemetryReasonDesc } from '../../shared/errors'
42+
import { fs } from '../../shared'
4243

4344
export async function listScanResults(
4445
client: DefaultCodeWhispererClient,
@@ -220,7 +221,7 @@ export async function getPresignedUrlAndUpload(
220221
throw new InvalidSourceZipError()
221222
}
222223
const srcReq: CreateUploadUrlRequest = {
223-
contentMd5: getMd5(zipMetadata.zipFilePath),
224+
contentMd5: await getMd5(zipMetadata.zipFilePath),
224225
artifactType: 'SourceCode',
225226
uploadIntent: getUploadIntent(scope),
226227
uploadContext: {
@@ -251,9 +252,9 @@ function getUploadIntent(scope: CodeWhispererConstants.CodeAnalysisScope): Uploa
251252
: CodeWhispererConstants.projectScanUploadIntent
252253
}
253254

254-
function getMd5(fileName: string) {
255+
async function getMd5(fileName: string) {
255256
const hasher = crypto.createHash('md5')
256-
hasher.update(readFileSync(fileName))
257+
hasher.update(await fs.readFileBytes(fileName))
257258
return hasher.digest('base64')
258259
}
259260

@@ -288,7 +289,7 @@ export async function uploadArtifactToS3(
288289
const logger = getLoggerForScope(scope)
289290
const encryptionContext = `{"uploadId":"${resp.uploadId}"}`
290291
const headersObj: Record<string, string> = {
291-
'Content-MD5': getMd5(fileName),
292+
'Content-MD5': await getMd5(fileName),
292293
'x-amz-server-side-encryption': 'aws:kms',
293294
'Content-Type': 'application/zip',
294295
'x-amz-server-side-encryption-context': Buffer.from(encryptionContext, 'utf8').toString('base64'),
@@ -300,7 +301,7 @@ export async function uploadArtifactToS3(
300301

301302
try {
302303
const response = await request.fetch('PUT', resp.uploadUrl, {
303-
body: readFileSync(fileName),
304+
body: await fs.readFileBytes(fileName),
304305
headers: resp?.requestHeaders ?? headersObj,
305306
}).response
306307
logger.debug(`StatusCode: ${response.status}, Text: ${response.statusText}`)

packages/core/src/codewhisperer/service/transformByQ/transformApiHandler.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55
import * as vscode from 'vscode'
6-
import * as nodefs from 'fs'
76
import * as path from 'path'
87
import * as os from 'os'
98
import * as codeWhisperer from '../../client/codewhisperer'
@@ -47,6 +46,7 @@ import { ExportIntent, TransformationDownloadArtifactType } from '@amzn/codewhis
4746
import fs from '../../../shared/fs/fs'
4847
import { ChatSessionManager } from '../../../amazonqGumby/chat/storages/chatSession'
4948
import { convertToTimeString, encodeHTML } from '../../../shared/utilities/textUtilities'
49+
import { readdirSync } from 'fs'
5050

5151
export function getSha256(buffer: Buffer) {
5252
const hasher = crypto.createHash('sha256')
@@ -109,7 +109,7 @@ export async function uploadArtifactToS3(
109109
) {
110110
throwIfCancelled()
111111
try {
112-
const uploadFileByteSize = (await nodefs.promises.stat(fileName)).size
112+
const uploadFileByteSize = (await fs.stat(fileName)).size
113113
getLogger().info(
114114
`Uploading project artifact at %s with checksum %s using uploadId: %s and size %s kB`,
115115
fileName,
@@ -249,7 +249,7 @@ function isExcludedDependencyFile(path: string): boolean {
249249
* getFilesRecursively on the source code folder.
250250
*/
251251
function getFilesRecursively(dir: string, isDependenciesFolder: boolean): string[] {
252-
const entries = nodefs.readdirSync(dir, { withFileTypes: true })
252+
const entries = readdirSync(dir, { withFileTypes: true })
253253
const files = entries.flatMap((entry) => {
254254
const res = path.resolve(dir, entry.name)
255255
// exclude 'target' directory from ZIP (except if zipping dependencies) due to issues in backend
@@ -301,14 +301,14 @@ export async function zipCode({ dependenciesFolder, humanInTheLoopFlag, modulePa
301301
const sourceFiles = getFilesRecursively(modulePath, false)
302302
let sourceFilesSize = 0
303303
for (const file of sourceFiles) {
304-
if (nodefs.statSync(file).isDirectory()) {
304+
if (await fs.existsDir(file)) {
305305
getLogger().info('CodeTransformation: Skipping directory, likely a symlink')
306306
continue
307307
}
308308
const relativePath = path.relative(modulePath, file)
309309
const paddedPath = path.join('sources', relativePath)
310310
zip.addLocalFile(file, path.dirname(paddedPath))
311-
sourceFilesSize += (await nodefs.promises.stat(file)).size
311+
sourceFilesSize += (await fs.stat(file)).size
312312
}
313313
getLogger().info(`CodeTransformation: source code files size = ${sourceFilesSize}`)
314314
}
@@ -330,7 +330,7 @@ export async function zipCode({ dependenciesFolder, humanInTheLoopFlag, modulePa
330330
// const paddedPath = path.join(`dependencies/${dependenciesFolder.name}`, relativePath)
331331
const paddedPath = path.join(`dependencies/`, relativePath)
332332
zip.addLocalFile(file, path.dirname(paddedPath))
333-
dependencyFilesSize += (await nodefs.promises.stat(file)).size
333+
dependencyFilesSize += (await fs.stat(file)).size
334334
}
335335
getLogger().info(`CodeTransformation: dependency files size = ${dependencyFilesSize}`)
336336
dependenciesCopied = true
@@ -365,7 +365,7 @@ export async function zipCode({ dependenciesFolder, humanInTheLoopFlag, modulePa
365365
}
366366
}
367367

368-
const zipSize = (await nodefs.promises.stat(tempFilePath)).size
368+
const zipSize = (await fs.stat(tempFilePath)).size
369369

370370
const exceedsLimit = zipSize > CodeWhispererConstants.uploadZipSizeLimitInBytes
371371

@@ -408,8 +408,8 @@ export async function startJob(uploadId: string) {
408408
}
409409
}
410410

411-
export function getImageAsBase64(filePath: string) {
412-
const fileContents = nodefs.readFileSync(filePath, { encoding: 'base64' })
411+
export async function getImageAsBase64(filePath: string) {
412+
const fileContents = Buffer.from(await fs.readFileBytes(filePath)).toString('base64')
413413
return `data:image/svg+xml;base64,${fileContents}`
414414
}
415415

@@ -418,7 +418,7 @@ export function getImageAsBase64(filePath: string) {
418418
* ex. getIcon('transform-file') returns the 'transform-file-light.svg' icon if user has a light theme enabled,
419419
* otherwise 'transform-file-dark.svg' is returned.
420420
*/
421-
export function getTransformationIcon(name: string) {
421+
export async function getTransformationIcon(name: string) {
422422
let iconPath = ''
423423
switch (name) {
424424
case 'linesOfCode':
@@ -448,7 +448,7 @@ export function getTransformationIcon(name: string) {
448448
} else {
449449
iconPath += '-dark.svg'
450450
}
451-
return getImageAsBase64(globals.context.asAbsolutePath(path.join('resources/icons/aws/amazonq', iconPath)))
451+
return await getImageAsBase64(globals.context.asAbsolutePath(path.join('resources/icons/aws/amazonq', iconPath)))
452452
}
453453

454454
export function getFormattedString(s: string) {
@@ -495,17 +495,17 @@ export function getTableMapping(stepZeroProgressUpdates: ProgressUpdates) {
495495
return map
496496
}
497497

498-
export function getJobStatisticsHtml(jobStatistics: any) {
498+
export async function getJobStatisticsHtml(jobStatistics: any) {
499499
let htmlString = ''
500500
if (jobStatistics.length === 0) {
501501
return htmlString
502502
}
503503
htmlString += `<div style="flex: 1; margin-left: 20px; border: 1px solid #424750; border-radius: 8px; padding: 10px;">`
504-
jobStatistics.forEach((stat: { name: string; value: string }) => {
505-
htmlString += `<p style="margin-bottom: 4px"><img src="${getTransformationIcon(
504+
for (const stat of jobStatistics) {
505+
htmlString += `<p style="margin-bottom: 4px"><img src="${await getTransformationIcon(
506506
stat.name
507507
)}" style="vertical-align: middle;"> ${getFormattedString(stat.name)}: ${stat.value}</p>`
508-
})
508+
}
509509
htmlString += `</div>`
510510
return htmlString
511511
}
@@ -533,9 +533,9 @@ export async function getTransformationPlan(jobId: string) {
533533
const jobStatistics = JSON.parse(tableMapping['0']).rows // ID of '0' reserved for job statistics table
534534

535535
// get logo directly since we only use one logo regardless of color theme
536-
const logoIcon = getTransformationIcon('transformLogo')
536+
const logoIcon = await getTransformationIcon('transformLogo')
537537

538-
const arrowIcon = getTransformationIcon('upArrow')
538+
const arrowIcon = await getTransformationIcon('upArrow')
539539

540540
let plan = `<style>table {border: 1px solid #424750;}</style>\n\n<a id="top"></a><br><p style="font-size: 24px;"><img src="${logoIcon}" style="margin-right: 15px; vertical-align: middle;"></img><b>${CodeWhispererConstants.planTitle}</b></p><br>`
541541
const authType = await getAuthType()
@@ -547,7 +547,7 @@ export async function getTransformationPlan(jobId: string) {
547547
}
548548
plan += `<div style="display: flex;"><div style="flex: 1; border: 1px solid #424750; border-radius: 8px; padding: 10px;"><p>${
549549
CodeWhispererConstants.planIntroductionMessage
550-
}</p></div>${getJobStatisticsHtml(jobStatistics)}</div>`
550+
}</p></div>${await getJobStatisticsHtml(jobStatistics)}</div>`
551551
plan += `<div style="margin-top: 32px; border: 1px solid #424750; border-radius: 8px; padding: 10px;"><p style="font-size: 18px; margin-bottom: 4px;"><b>${CodeWhispererConstants.planHeaderMessage}</b></p><i>${CodeWhispererConstants.planDisclaimerMessage} <a href="https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/code-transformation.html">Read more.</a></i><br><br>`
552552
response.transformationPlan.transformationSteps.slice(1).forEach((step) => {
553553
plan += `<div style="border: 1px solid #424750; border-radius: 8px; padding: 20px;"><div style="display:flex; justify-content:space-between; align-items:center;"><p style="font-size: 16px; margin-bottom: 4px;">${step.name}</p><a href="#top">Scroll to top <img src="${arrowIcon}" style="vertical-align: middle"></a></div><p>${step.description}</p>`

packages/core/src/codewhisperer/service/transformByQ/transformFileHandler.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import * as path from 'path'
88
import * as os from 'os'
99
import xml2js = require('xml2js')
1010
import * as CodeWhispererConstants from '../../models/constants'
11-
import { existsSync, writeFileSync } from 'fs'
1211
import { BuildSystem, FolderInfo, transformByQState } from '../../models/model'
1312
import { IManifestFile } from '../../../amazonqFeatureDev/models'
1413
import fs from '../../../shared/fs/fs'
@@ -25,13 +24,13 @@ export function getDependenciesFolderInfo(): FolderInfo {
2524

2625
export async function writeLogs() {
2726
const logFilePath = path.join(os.tmpdir(), 'build-logs.txt')
28-
writeFileSync(logFilePath, transformByQState.getErrorLog())
27+
await fs.writeFile(logFilePath, transformByQState.getErrorLog())
2928
return logFilePath
3029
}
3130

3231
export async function checkBuildSystem(projectPath: string) {
3332
const mavenBuildFilePath = path.join(projectPath, 'pom.xml')
34-
if (existsSync(mavenBuildFilePath)) {
33+
if (await fs.exists(mavenBuildFilePath)) {
3534
return BuildSystem.Maven
3635
}
3736
return BuildSystem.Unknown
@@ -55,7 +54,7 @@ export async function createPomCopy(
5554
export async function replacePomVersion(pomFileVirtualFileReference: vscode.Uri, version: string, delimiter: string) {
5655
const pomFileText = await fs.readFileText(pomFileVirtualFileReference.fsPath)
5756
const pomFileTextWithNewVersion = pomFileText.replace(delimiter, version)
58-
writeFileSync(pomFileVirtualFileReference.fsPath, pomFileTextWithNewVersion)
57+
await fs.writeFile(pomFileVirtualFileReference.fsPath, pomFileTextWithNewVersion)
5958
}
6059

6160
export async function getJsonValuesFromManifestFile(

0 commit comments

Comments
 (0)