Skip to content

Commit 70a53a5

Browse files
authored
browser: remove "got" in codewhisperer #4291
`got` package is not available in the browser. Replace usage with common `fetch` from `request.ts` Also: - refactor request.ts fetch for testing and current usages of the common fetch. - minor code refactors around some usage of the fetch code.
1 parent d6bfc0d commit 70a53a5

File tree

10 files changed

+72
-76
lines changed

10 files changed

+72
-76
lines changed

src/amazonqFeatureDev/util/upload.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import got from 'got'
7-
6+
import request, { RequestError } from '../../common/request'
87
import { getLogger } from '../../shared/logger/logger'
98
import { featureName } from '../constants'
109

@@ -17,8 +16,7 @@ import { UploadCodeError } from '../errors'
1716
*/
1817
export async function uploadCode(url: string, buffer: Buffer, checksumSha256: string, kmsKeyArn?: string) {
1918
try {
20-
await got(url, {
21-
method: 'PUT',
19+
await request.fetch('PUT', url, {
2220
body: buffer,
2321
headers: {
2422
'Content-Type': 'application/zip',
@@ -29,9 +27,11 @@ export async function uploadCode(url: string, buffer: Buffer, checksumSha256: st
2927
'x-amz-server-side-encryption': 'aws:kms',
3028
}),
3129
},
32-
})
30+
}).response
3331
} catch (e: any) {
3432
getLogger().error(`${featureName}: failed to upload code to s3: ${(e as Error).message}`)
35-
throw new UploadCodeError(e instanceof got.HTTPError ? `${e.response.statusCode}` : 'Unknown')
33+
throw new UploadCodeError(
34+
e instanceof RequestError ? `${e.response.status}: ${e.response.statusText}` : 'Unknown'
35+
)
3636
}
3737
}

src/applicationcomposer/webviewManager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import * as vscode from 'vscode'
77
import * as nls from 'vscode-nls'
8-
import fetch from '../common/request'
8+
import request from '../common/request'
99
import { ApplicationComposer } from './composerWebview'
1010
import { getLogger } from '../shared/logger'
1111

@@ -34,7 +34,7 @@ export class ApplicationComposerManager {
3434

3535
private async fetchWebviewHtml() {
3636
const source = isLocalDev ? localhost : cdn
37-
const response = await fetch('GET', `${source}/index.html`).response
37+
const response = await request.fetch('GET', `${source}/index.html`).response
3838
this.webviewHtml = await response.text()
3939
for (const visualization of this.managedVisualizations.values()) {
4040
await visualization.refreshPanel(this.extensionContext)

src/codewhisperer/service/securityScanHandler.ts

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ import * as codewhispererClient from '../client/codewhisperer'
1111
import * as CodeWhispererConstants from '../models/constants'
1212
import { existsSync, statSync, readFileSync } from 'fs'
1313
import { RawCodeScanIssue } from '../models/model'
14-
import got from 'got'
1514
import * as crypto from 'crypto'
1615
import path = require('path')
1716
import { pageableToCollection } from '../../shared/utilities/collectionUtils'
1817
import { ArtifactMap, CreateUploadUrlRequest, CreateUploadUrlResponse } from '../client/codewhispereruserclient'
1918
import { Truncation } from '../util/dependencyGraph/dependencyGraph'
2019
import { TelemetryHelper } from '../util/telemetryHelper'
20+
import request from '../../common/request'
2121

2222
export async function listScanResults(
2323
client: DefaultCodeWhispererClient,
@@ -176,25 +176,20 @@ export function throwIfCancelled() {
176176

177177
export async function uploadArtifactToS3(fileName: string, resp: CreateUploadUrlResponse) {
178178
const encryptionContext = `{"uploadId":"${resp.uploadId}"}`
179-
const headersObj =
180-
resp.kmsKeyArn !== '' || resp.kmsKeyArn !== undefined
181-
? {
182-
'Content-MD5': getMd5(fileName),
183-
'x-amz-server-side-encryption': 'aws:kms',
184-
'Content-Type': 'application/zip',
185-
'x-amz-server-side-encryption-aws-kms-key-id': resp.kmsKeyArn,
186-
'x-amz-server-side-encryption-context': Buffer.from(encryptionContext, 'utf8').toString('base64'),
187-
}
188-
: {
189-
'Content-MD5': getMd5(fileName),
190-
'x-amz-server-side-encryption': 'aws:kms',
191-
'Content-Type': 'application/zip',
192-
'x-amz-server-side-encryption-context': Buffer.from(encryptionContext, 'utf8').toString('base64'),
193-
}
194-
const response = await got(resp.uploadUrl, {
195-
method: 'PUT',
179+
const headersObj: Record<string, string> = {
180+
'Content-MD5': getMd5(fileName),
181+
'x-amz-server-side-encryption': 'aws:kms',
182+
'Content-Type': 'application/zip',
183+
'x-amz-server-side-encryption-context': Buffer.from(encryptionContext, 'utf8').toString('base64'),
184+
}
185+
186+
if (resp.kmsKeyArn !== '' && resp.kmsKeyArn !== undefined) {
187+
headersObj['x-amz-server-side-encryption-aws-kms-key-id'] = resp.kmsKeyArn
188+
}
189+
190+
const response = await request.fetch('PUT', resp.uploadUrl, {
196191
body: readFileSync(fileName),
197192
headers: headersObj,
198-
})
199-
getLogger().debug(`StatusCode: ${response.statusCode}`)
193+
}).response
194+
getLogger().debug(`StatusCode: ${response.status}, Text: ${response.statusText}`)
200195
}

src/codewhisperer/service/transformByQHandler.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ import * as os from 'os'
1616
import * as vscode from 'vscode'
1717
import { spawnSync } from 'child_process'
1818
import AdmZip from 'adm-zip'
19-
import fetch from '../../common/request'
2019
import globals from '../../shared/extensionGlobals'
2120
import { CodeTransformMavenBuildCommand, telemetry } from '../../shared/telemetry/telemetry'
2221
import { ToolkitError } from '../../shared/errors'
2322
import { codeTransformTelemetryState } from '../../amazonqGumby/telemetry/codeTransformTelemetryState'
2423
import { calculateTotalLatency, javapOutputToTelemetryValue } from '../../amazonqGumby/telemetry/codeTransformTelemetry'
2524
import { TransformByQJavaProjectNotFound } from '../../amazonqGumby/models/model'
2625
import { MetadataResult } from '../../shared/telemetry/telemetryClient'
26+
import request from '../../common/request'
2727

2828
/* TODO: once supported in all browsers and past "experimental" mode, use Intl DurationFormat:
2929
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DurationFormat#browser_compatibility
@@ -178,8 +178,10 @@ export async function uploadArtifactToS3(fileName: string, resp: CreateUploadUrl
178178
throwIfCancelled()
179179
try {
180180
const apiStartTime = Date.now()
181-
const response = await fetch('PUT', resp.uploadUrl, { body: fs.readFileSync(fileName), headers: headersObj })
182-
.response
181+
const response = await request.fetch('PUT', resp.uploadUrl, {
182+
body: fs.readFileSync(fileName),
183+
headers: headersObj,
184+
}).response
183185
telemetry.codeTransform_logApiLatency.emit({
184186
codeTransformApiNames: 'UploadZip',
185187
codeTransformSessionId: codeTransformTelemetryState.getSessionId(),

src/common/request.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,28 @@
55

66
import crossFetch from 'cross-fetch'
77

8-
/**
9-
* Make a fetch request.
10-
*
11-
* @example
12-
* const request = fetch('GET', 'https://example.com')
13-
* setTimeout(() => request.cancel(), 10_000)
14-
* const response = await request.response
15-
* const text = await response.text()
16-
*
17-
* @param wrappedFetch - The actual fetch implementation
18-
*/
19-
export default function fetch(
20-
method: RequestMethod,
21-
url: string,
22-
params?: RequestParamsArg,
23-
wrappedFetch = crossFetch
24-
): FetchRequest {
25-
return new FetchRequest(url, { ...params, method }, wrappedFetch)
8+
const request = {
9+
/**
10+
* Make a fetch request.
11+
*
12+
* @example
13+
* const request = fetch('GET', 'https://example.com')
14+
* setTimeout(() => request.cancel(), 10_000)
15+
* const response = await request.response
16+
* const text = await response.text()
17+
*
18+
* @param wrappedFetch - The actual fetch implementation
19+
*/
20+
fetch: function (
21+
method: RequestMethod,
22+
url: string,
23+
params?: RequestParamsArg,
24+
wrappedFetch = crossFetch
25+
): FetchRequest {
26+
return new FetchRequest(url, { ...params, method }, wrappedFetch)
27+
},
2628
}
29+
export default request
2730

2831
type RequestMethod = 'GET' | 'POST' | 'PUT'
2932
/** All possible params of a fetch request (eg: headers) */

src/test/amazonqFeatureDev/session/sessionState.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import * as vscode from 'vscode'
77
import assert from 'assert'
88
import sinon from 'sinon'
9-
import * as got from 'got'
109
import { RefinementState, PrepareRefinementState } from '../../../amazonqFeatureDev/session/sessionState'
1110
import { SessionStateConfig, SessionStateAction } from '../../../amazonqFeatureDev/types'
1211
import { Messenger } from '../../../amazonqFeatureDev/controllers/chat/messenger/messenger'
@@ -16,6 +15,7 @@ import { FeatureDevClient } from '../../../amazonqFeatureDev/client/featureDev'
1615
import { PrepareRepoFailedError } from '../../../amazonqFeatureDev/errors'
1716
import { TelemetryHelper } from '../../../amazonqFeatureDev/util/telemetryHelper'
1817
import { assertTelemetry } from '../../testUtil'
18+
import { getFetchStubWithResponse } from '../../common/request.test'
1919

2020
const mockSessionStateAction = (msg?: string): SessionStateAction => {
2121
return {
@@ -75,7 +75,7 @@ describe('sessionState', () => {
7575
sinon.stub(vscode.workspace, 'findFiles').resolves([])
7676
mockCreateUploadUrl = sinon.stub().resolves({ uploadId: '', uploadUrl: '' })
7777
mockGeneratePlan = sinon.stub().resolves(testApproach)
78-
sinon.stub(got, 'default').resolves({ statusCode: 200 })
78+
getFetchStubWithResponse({ status: 200 })
7979

8080
const testAction = mockSessionStateAction()
8181
await new PrepareRefinementState(testConfig, testApproach, tabId).interact(testAction)

src/test/codewhisperer/commands/startSecurityScan.test.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import assert from 'assert'
77
import * as vscode from 'vscode'
88
import * as sinon from 'sinon'
9-
import * as got from 'got'
109
import * as semver from 'semver'
1110
import { DefaultCodeWhispererClient } from '../../../codewhisperer/client/codewhisperer'
1211
import * as startSecurityScan from '../../../codewhisperer/commands/startSecurityScan'
@@ -21,14 +20,14 @@ import { HttpResponse } from 'aws-sdk'
2120
import { getTestWindow } from '../../shared/vscode/window'
2221
import { SeverityLevel } from '../../shared/vscode/message'
2322
import { cancel } from '../../../shared/localizedText'
24-
import { sleep } from '../../../shared/utilities/timeoutUtils'
2523
import {
2624
codeScanLogsOutputChannelId,
2725
showScannedFilesMessage,
2826
stopScanMessage,
2927
} from '../../../codewhisperer/models/constants'
3028
import * as model from '../../../codewhisperer/models/model'
3129
import { CodewhispererSecurityScan } from '../../../shared/telemetry/telemetry.gen'
30+
import { getFetchStubWithResponse } from '../../common/request.test'
3231

3332
const mockCreateCodeScanResponse = {
3433
$response: {
@@ -177,7 +176,7 @@ describe('startSecurityScan', function () {
177176
})
178177

179178
it('Should render security scan result', async function () {
180-
sinon.stub(got, 'default').resolves({ statusCode: 200 })
179+
getFetchStubWithResponse({ status: 200, statusText: 'testing stub' })
181180
const commandSpy = sinon.spy(vscode.commands, 'executeCommand')
182181
const securityScanRenderSpy = sinon.spy(diagnosticsProvider, 'initSecurityScanRender')
183182

@@ -194,11 +193,7 @@ describe('startSecurityScan', function () {
194193
})
195194

196195
it('Should stop security scan', async function () {
197-
sinon.stub(got, 'default').callsFake(() => {
198-
return sleep(1000).then(() => {
199-
return { statusCode: 200 }
200-
}) as any
201-
})
196+
getFetchStubWithResponse({ status: 200, statusText: 'testing stub' })
202197
const securityScanRenderSpy = sinon.spy(diagnosticsProvider, 'initSecurityScanRender')
203198
const securityScanStoppedErrorSpy = sinon.spy(model, 'CodeScanStoppedError')
204199
const testWindow = getTestWindow()
@@ -223,11 +218,7 @@ describe('startSecurityScan', function () {
223218
})
224219

225220
it('Should not stop security scan when not confirmed', async function () {
226-
sinon.stub(got, 'default').callsFake(() => {
227-
return sleep(1000).then(() => {
228-
return { statusCode: 200 }
229-
}) as any
230-
})
221+
getFetchStubWithResponse({ status: 200, statusText: 'testing stub' })
231222
const securityScanRenderSpy = sinon.spy(diagnosticsProvider, 'initSecurityScanRender')
232223
const securityScanStoppedErrorSpy = sinon.spy(model, 'CodeScanStoppedError')
233224
const testWindow = getTestWindow()
@@ -256,7 +247,7 @@ describe('startSecurityScan', function () {
256247
this.skip()
257248
}
258249
const commandSpy = sinon.spy(vscode.commands, 'executeCommand')
259-
sinon.stub(got, 'default').resolves({ statusCode: 200 })
250+
getFetchStubWithResponse({ status: 200, statusText: 'testing stub' })
260251
const testWindow = getTestWindow()
261252
testWindow.onDidShowMessage(message => {
262253
if (message.message.includes('Security scan completed')) {

src/test/common/request.test.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@
66
import { SinonStub, stub } from 'sinon'
77
import assert from 'assert'
88
import crossFetch from 'cross-fetch'
9-
import fetch, { RequestCancelledError, RequestError } from '../../common/request'
9+
import request, { RequestCancelledError, RequestError } from '../../common/request'
1010
import globals from '../../shared/extensionGlobals'
1111

12+
// Returns a stubbed fetch for other tests.
13+
export function getFetchStubWithResponse(response: Partial<Response>) {
14+
return stub(request, 'fetch').returns({ response: new Promise((res, _) => res(response)) } as any)
15+
}
16+
1217
describe('fetch()', function () {
1318
/** We built a wrapper around an actual fetch implementation, this is a fake stub of it for testing. */
1419
let wrappedFetch: SinonStub<Parameters<typeof crossFetch>, Promise<Response>>
@@ -25,7 +30,7 @@ describe('fetch()', function () {
2530
})
2631

2732
it('passes the expected arguments to the wrapped fetch implementation', async function () {
28-
const response = await fetch('GET', 'http://test.com', { mode: 'cors' }, wrappedFetch).response
33+
const response = await request.fetch('GET', 'http://test.com', { mode: 'cors' }, wrappedFetch).response
2934

3035
assert.strictEqual(wrappedFetch.callCount, 1)
3136
assert.deepStrictEqual(wrappedFetch.getCall(0).args, [
@@ -44,10 +49,10 @@ describe('fetch()', function () {
4449
status: testStatusCode,
4550
} as Response)
4651

47-
const request = fetch('GET', 'http://test.com', {}, wrappedFetch)
52+
const req = request.fetch('GET', 'http://test.com', {}, wrappedFetch)
4853

4954
await assert.rejects(async () => {
50-
await request.response
55+
await req.response
5156
}, RequestError)
5257
})
5358

@@ -64,16 +69,16 @@ describe('fetch()', function () {
6469
// - Once all microTasks are completed/exhausted, only then will the event loop
6570
// pickup a single macrotask. This process then repeats.
6671

67-
const request = fetch('GET', 'https://aws.amazon.com/', {})
72+
const req = request.fetch('GET', 'https://aws.amazon.com/', {})
6873

6974
// cancel() call is part of the macrotask queue.
7075
globals.clock.setTimeout(() => {
71-
request.cancel()
76+
req.cancel()
7277
}, 0)
7378

7479
// The response and the wrapped fetch request pushed to the microtask queue.
7580
await assert.rejects(
76-
() => request.response,
81+
() => req.response,
7782
e => {
7883
return e instanceof RequestCancelledError
7984
}

src/testE2E/amazonqGumby/transformByQ.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import assert from 'assert'
77
import { getSha256, uploadArtifactToS3, zipCode } from '../../codewhisperer/service/transformByQHandler'
8-
import fetch from '../../common/request'
8+
import request from '../../common/request'
99
import * as CodeWhispererConstants from '../../codewhisperer/models/constants'
1010
import * as codeWhisperer from '../../codewhisperer/client/codewhisperer'
1111
import * as os from 'os'
@@ -54,7 +54,7 @@ describe('transformByQ', async function () {
5454
}
5555
await assert.rejects(
5656
() =>
57-
fetch('PUT', response.uploadUrl, { body: fs.readFileSync(zippedCodePath), headers: headersObj })
57+
request.fetch('PUT', response.uploadUrl, { body: fs.readFileSync(zippedCodePath), headers: headersObj })
5858
.response
5959
)
6060
})

src/testInteg/common/request.ts

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

66
import { endpointsFileUrl } from '../../shared/constants'
7-
import fetch from '../../common/request'
7+
import request from '../../common/request'
88
import assert from 'assert'
99

1010
describe('fetch()', function () {
1111
it('makes a fetch request', async function () {
12-
const response = await fetch('GET', endpointsFileUrl).response
12+
const response = await request.fetch('GET', endpointsFileUrl).response
1313
assert.strictEqual(response.ok, true)
1414
assert.strictEqual(response.status, 200)
1515
})

0 commit comments

Comments
 (0)