Skip to content

Commit 96b5351

Browse files
Copiloticlanton
andauthored
Fix CI: Move WebClient private members to module-level for rush-sdk type compatibility
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/d8f5e137-3cb0-4ed9-8391-50d7a33d804e Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
1 parent 8becd08 commit 96b5351

1 file changed

Lines changed: 92 additions & 77 deletions

File tree

libraries/rush-lib/src/utilities/WebClient.ts

Lines changed: 92 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -344,13 +344,93 @@ const makeStreamRequestAsync: StreamFetchFn = async (
344344
);
345345
};
346346

347+
// Module-level mutable state for mock injection. These must NOT be private members
348+
// of WebClient because rush-sdk re-exports WebClient as a separate type declaration,
349+
// and TypeScript's structural typing treats private members nominally, causing type
350+
// incompatibility between the rush-lib and rush-sdk versions.
351+
let _requestFnAsync: FetchFn = makeRequestAsync;
352+
let _streamRequestFnAsync: StreamFetchFn = makeStreamRequestAsync;
353+
354+
function _mergeHeaders(target: Record<string, string>, source: Record<string, string>): void {
355+
for (const [name, value] of Object.entries(source)) {
356+
target[name] = value;
357+
}
358+
}
359+
360+
/**
361+
* Builds the low-level IRequestOptions from WebClient instance state and caller-provided options.
362+
* This is a module-level function (not a private method) to avoid the rush-sdk type mismatch.
363+
*/
364+
function buildRequestOptions(
365+
webClient: WebClient,
366+
options?: IGetFetchOptions | IFetchOptionsWithBody
367+
): IRequestOptions {
368+
const {
369+
headers: optionsHeaders,
370+
timeoutMs = 15 * 1000,
371+
verb,
372+
redirect,
373+
body,
374+
noDecode
375+
} = (options as IFetchOptionsWithBody | undefined) ?? {};
376+
377+
const headers: Record<string, string> = {};
378+
379+
const { standardHeaders, userAgent, accept, proxy } = webClient;
380+
381+
_mergeHeaders(headers, standardHeaders);
382+
383+
if (optionsHeaders) {
384+
_mergeHeaders(headers, optionsHeaders);
385+
}
386+
387+
if (userAgent) {
388+
headers[USER_AGENT_HEADER_NAME] = userAgent;
389+
}
390+
391+
if (accept) {
392+
headers[ACCEPT_HEADER_NAME] = accept;
393+
}
394+
395+
let proxyUrl: string = '';
396+
397+
switch (proxy) {
398+
case WebClientProxy.Detect:
399+
if (process.env.HTTPS_PROXY) {
400+
proxyUrl = process.env.HTTPS_PROXY;
401+
} else if (process.env.HTTP_PROXY) {
402+
proxyUrl = process.env.HTTP_PROXY;
403+
}
404+
break;
405+
406+
case WebClientProxy.Fiddler:
407+
// For debugging, disable cert validation
408+
// eslint-disable-next-line
409+
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
410+
proxyUrl = 'http://localhost:8888/';
411+
break;
412+
}
413+
414+
let agent: HttpAgent | undefined = undefined;
415+
if (proxyUrl) {
416+
agent = createHttpsProxyAgent(proxyUrl);
417+
}
418+
419+
return {
420+
method: verb,
421+
headers,
422+
agent,
423+
timeout: timeoutMs,
424+
redirect,
425+
body,
426+
noDecode
427+
};
428+
}
429+
347430
/**
348431
* A helper for issuing HTTP requests.
349432
*/
350433
export class WebClient {
351-
private static _requestFnAsync: FetchFn = makeRequestAsync;
352-
private static _streamRequestFnAsync: StreamFetchFn = makeStreamRequestAsync;
353-
354434
public readonly standardHeaders: Record<string, string> = {};
355435

356436
public accept: string | undefined = '*/*';
@@ -359,25 +439,23 @@ export class WebClient {
359439
public proxy: WebClientProxy = WebClientProxy.Detect;
360440

361441
public static mockRequestFn(fn: FetchFn): void {
362-
WebClient._requestFnAsync = fn;
442+
_requestFnAsync = fn;
363443
}
364444

365445
public static resetMockRequestFn(): void {
366-
WebClient._requestFnAsync = makeRequestAsync;
446+
_requestFnAsync = makeRequestAsync;
367447
}
368448

369449
public static mockStreamRequestFn(fn: StreamFetchFn): void {
370-
WebClient._streamRequestFnAsync = fn;
450+
_streamRequestFnAsync = fn;
371451
}
372452

373453
public static resetMockStreamRequestFn(): void {
374-
WebClient._streamRequestFnAsync = makeStreamRequestAsync;
454+
_streamRequestFnAsync = makeStreamRequestAsync;
375455
}
376456

377457
public static mergeHeaders(target: Record<string, string>, source: Record<string, string>): void {
378-
for (const [name, value] of Object.entries(source)) {
379-
target[name] = value;
380-
}
458+
_mergeHeaders(target, source);
381459
}
382460

383461
public addBasicAuthHeader(userName: string, password: string): void {
@@ -389,8 +467,8 @@ export class WebClient {
389467
url: string,
390468
options?: IGetFetchOptions | IFetchOptionsWithBody
391469
): Promise<IWebClientResponse> {
392-
const requestInit: IRequestOptions = this._buildRequestOptions(options);
393-
return await WebClient._requestFnAsync(url, requestInit);
470+
const requestInit: IRequestOptions = buildRequestOptions(this, options);
471+
return await _requestFnAsync(url, requestInit);
394472
}
395473

396474
/**
@@ -401,70 +479,7 @@ export class WebClient {
401479
url: string,
402480
options?: IGetFetchOptions | IFetchOptionsWithBody
403481
): Promise<IWebClientStreamResponse> {
404-
const requestInit: IRequestOptions = this._buildRequestOptions(options);
405-
return await WebClient._streamRequestFnAsync(url, requestInit);
406-
}
407-
408-
private _buildRequestOptions(options?: IGetFetchOptions | IFetchOptionsWithBody): IRequestOptions {
409-
const {
410-
headers: optionsHeaders,
411-
timeoutMs = 15 * 1000,
412-
verb,
413-
redirect,
414-
body,
415-
noDecode
416-
} = (options as IFetchOptionsWithBody | undefined) ?? {};
417-
418-
const headers: Record<string, string> = {};
419-
420-
const { standardHeaders, userAgent, accept, proxy } = this;
421-
422-
WebClient.mergeHeaders(headers, standardHeaders);
423-
424-
if (optionsHeaders) {
425-
WebClient.mergeHeaders(headers, optionsHeaders);
426-
}
427-
428-
if (userAgent) {
429-
headers[USER_AGENT_HEADER_NAME] = userAgent;
430-
}
431-
432-
if (accept) {
433-
headers[ACCEPT_HEADER_NAME] = accept;
434-
}
435-
436-
let proxyUrl: string = '';
437-
438-
switch (proxy) {
439-
case WebClientProxy.Detect:
440-
if (process.env.HTTPS_PROXY) {
441-
proxyUrl = process.env.HTTPS_PROXY;
442-
} else if (process.env.HTTP_PROXY) {
443-
proxyUrl = process.env.HTTP_PROXY;
444-
}
445-
break;
446-
447-
case WebClientProxy.Fiddler:
448-
// For debugging, disable cert validation
449-
// eslint-disable-next-line
450-
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
451-
proxyUrl = 'http://localhost:8888/';
452-
break;
453-
}
454-
455-
let agent: HttpAgent | undefined = undefined;
456-
if (proxyUrl) {
457-
agent = createHttpsProxyAgent(proxyUrl);
458-
}
459-
460-
return {
461-
method: verb,
462-
headers,
463-
agent,
464-
timeout: timeoutMs,
465-
redirect,
466-
body,
467-
noDecode
468-
};
482+
const requestInit: IRequestOptions = buildRequestOptions(this, options);
483+
return await _streamRequestFnAsync(url, requestInit);
469484
}
470485
}

0 commit comments

Comments
 (0)