@@ -5,9 +5,12 @@ import { MockedRequest } from 'msw';
55import { SetupServerApi } from 'msw/node' ;
66import { IsomorphicResponse } from '@mswjs/interceptors' ;
77
8+ import { Headers } from 'headers-polyfill' ;
9+ import fetch from 'node-fetch' ;
10+
811import { DisposableObject } from '../pure/disposable-object' ;
912
10- import { GitHubApiRequest , RequestKind } from './gh-api-request' ;
13+ import { GetVariantAnalysisRepoResultRequest , GitHubApiRequest , RequestKind } from './gh-api-request' ;
1114
1215export 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 ( / o b j e c t s - o r i g i n \. g i t h u b u s e r c o n t e n t \. c o m \/ c o d e q l - q u e r y - c o n s o l e \/ c o d e q l - v a r i a n t - a n a l y s i s - r e p o - t a s k s \/ \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+ }
0 commit comments