Skip to content

Commit f0f0172

Browse files
authored
Merge pull request #1648 from github/koesie10/store-scenario-bodies-as-files
Store binary scenario bodies as files
2 parents c8b0461 + 00de082 commit f0f0172

File tree

3 files changed

+62
-7
lines changed

3 files changed

+62
-7
lines changed

extensions/ql-vscode/src/mocks/gh-api-request.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ export interface GetVariantAnalysisRepoResultRequest {
6464
},
6565
response: {
6666
status: number,
67-
body?: ArrayBuffer
67+
body?: Buffer | string,
68+
contentType: string,
6869
}
6970
}
7071

extensions/ql-vscode/src/mocks/recorder.ts

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import { MockedRequest } from 'msw';
55
import { SetupServerApi } from 'msw/node';
66
import { IsomorphicResponse } from '@mswjs/interceptors';
77

8+
import { Headers } from 'headers-polyfill';
9+
import fetch from 'node-fetch';
10+
811
import { DisposableObject } from '../pure/disposable-object';
912

10-
import { GitHubApiRequest, RequestKind } from './gh-api-request';
13+
import { GetVariantAnalysisRepoResultRequest, GitHubApiRequest, RequestKind } from './gh-api-request';
1114

1215
export class Recorder extends DisposableObject {
1316
private readonly allRequests = new Map<string, MockedRequest>();
@@ -70,7 +73,28 @@ export class Recorder extends DisposableObject {
7073

7174
const fileName = `${i}-${request.request.kind}.json`;
7275
const filePath = path.join(scenarioDirectory, fileName);
73-
await fs.writeFile(filePath, JSON.stringify(request, null, 2));
76+
77+
let writtenRequest = {
78+
...request
79+
};
80+
81+
if (shouldWriteBodyToFile(writtenRequest)) {
82+
const extension = writtenRequest.response.contentType === 'application/zip' ? 'zip' : 'bin';
83+
84+
const bodyFileName = `${i}-${writtenRequest.request.kind}.body.${extension}`;
85+
const bodyFilePath = path.join(scenarioDirectory, bodyFileName);
86+
await fs.writeFile(bodyFilePath, writtenRequest.response.body);
87+
88+
writtenRequest = {
89+
...writtenRequest,
90+
response: {
91+
...writtenRequest.response,
92+
body: `file:${bodyFileName}`,
93+
},
94+
};
95+
}
96+
97+
await fs.writeFile(filePath, JSON.stringify(writtenRequest, null, 2));
7498
}
7599

76100
this.stop();
@@ -79,10 +103,14 @@ export class Recorder extends DisposableObject {
79103
}
80104

81105
private onRequestStart(request: MockedRequest): void {
106+
if (request.headers.has('x-vscode-codeql-msw-bypass')) {
107+
return;
108+
}
109+
82110
this.allRequests.set(request.id, request);
83111
}
84112

85-
private onResponseBypass(response: IsomorphicResponse, requestId: string): void {
113+
private async onResponseBypass(response: IsomorphicResponse, requestId: string): Promise<void> {
86114
const request = this.allRequests.get(requestId);
87115
this.allRequests.delete(requestId);
88116
if (!request) {
@@ -93,7 +121,7 @@ export class Recorder extends DisposableObject {
93121
return;
94122
}
95123

96-
const gitHubApiRequest = createGitHubApiRequest(request.url.toString(), response.status, response.body);
124+
const gitHubApiRequest = await createGitHubApiRequest(request.url.toString(), response.status, response.body, response.headers);
97125
if (!gitHubApiRequest) {
98126
return;
99127
}
@@ -102,7 +130,7 @@ export class Recorder extends DisposableObject {
102130
}
103131
}
104132

105-
function createGitHubApiRequest(url: string, status: number, body: string): GitHubApiRequest | undefined {
133+
async function createGitHubApiRequest(url: string, status: number, body: string, headers: Headers): Promise<GitHubApiRequest | undefined> {
106134
if (!url) {
107135
return undefined;
108136
}
@@ -160,17 +188,33 @@ function createGitHubApiRequest(url: string, status: number, body: string): GitH
160188
// if url is a download URL for a variant analysis result, then it's a get-variant-analysis-repoResult.
161189
const repoDownloadMatch = url.match(/objects-origin\.githubusercontent\.com\/codeql-query-console\/codeql-variant-analysis-repo-tasks\/\d+\/(?<repositoryId>\d+)/);
162190
if (repoDownloadMatch?.groups?.repositoryId) {
191+
// msw currently doesn't support binary response bodies, so we need to download this separately
192+
// see https://github.com/mswjs/interceptors/blob/15eafa6215a328219999403e3ff110e71699b016/src/interceptors/ClientRequest/utils/getIncomingMessageBody.ts#L24-L33
193+
// Essentially, mws is trying to decode a ZIP file as UTF-8 which changes the bytes and corrupts the file.
194+
const response = await fetch(url, {
195+
headers: {
196+
// We need to ensure we don't end up in an infinite loop, since this request will also be intercepted
197+
'x-vscode-codeql-msw-bypass': 'true',
198+
},
199+
});
200+
const responseBuffer = await response.buffer();
201+
163202
return {
164203
request: {
165204
kind: RequestKind.GetVariantAnalysisRepoResult,
166205
repositoryId: parseInt(repoDownloadMatch.groups.repositoryId, 10),
167206
},
168207
response: {
169208
status,
170-
body: body as unknown as ArrayBuffer,
209+
body: responseBuffer,
210+
contentType: headers.get('content-type') ?? 'application/octet-stream',
171211
}
172212
};
173213
}
174214

175215
return undefined;
176216
}
217+
218+
function shouldWriteBodyToFile(request: GitHubApiRequest): request is GetVariantAnalysisRepoResultRequest {
219+
return request.response.body instanceof Buffer;
220+
}

extensions/ql-vscode/src/mocks/request-handlers.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,17 @@ async function readRequestFiles(scenarioDirPath: string): Promise<GitHubApiReque
3939

4040
const requests: GitHubApiRequest[] = [];
4141
for (const file of orderedFiles) {
42+
if (!file.endsWith('.json')) {
43+
continue;
44+
}
45+
4246
const filePath = path.join(scenarioDirPath, file);
4347
const request: GitHubApiRequest = await fs.readJson(filePath, { encoding: 'utf8' });
48+
49+
if (typeof request.response.body === 'string' && request.response.body.startsWith('file:')) {
50+
request.response.body = await fs.readFile(path.join(scenarioDirPath, request.response.body.substring(5)));
51+
}
52+
4453
requests.push(request);
4554
}
4655

@@ -135,6 +144,7 @@ function createGetVariantAnalysisRepoResultRequestHandler(requests: GitHubApiReq
135144
if (scenarioRequest.response.body) {
136145
return res(
137146
ctx.status(scenarioRequest.response.status),
147+
ctx.set('Content-Type', scenarioRequest.response.contentType),
138148
ctx.body(scenarioRequest.response.body),
139149
);
140150
} else {

0 commit comments

Comments
 (0)