Skip to content

Commit

Permalink
refactor: Clean-up Types & Resolve lint warnings
Browse files Browse the repository at this point in the history
  • Loading branch information
danielbankhead committed May 16, 2024
1 parent 4e6de5f commit e6c2371
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 99 deletions.
113 changes: 31 additions & 82 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export class GaxiosError<T = any> extends Error {

constructor(
message: string,
public config: GaxiosOptions,
public config: GaxiosOptionsPrepared,
public response?: GaxiosResponse<T>,
public error?: Error | NodeJS.ErrnoException
) {
Expand Down Expand Up @@ -111,43 +111,41 @@ export class GaxiosError<T = any> extends Error {
}

if (config.errorRedactor) {
config.errorRedactor<T>({
config.errorRedactor({
config: this.config,
response: this.response,
});
}
}
}

/**
* @deprecated use native {@link globalThis.Headers}.
*/
export interface Headers {
[index: string]: any;
}
export type GaxiosPromise<T = any> = Promise<GaxiosResponse<T>>;
type GaxiosResponseData =
| ReturnType<JSON['parse']>
| GaxiosOptionsPrepared['data'];

export type GaxiosPromise<T = GaxiosResponseData> = Promise<GaxiosResponse<T>>;

export interface GaxiosResponse<T = any> extends Response {
export interface GaxiosResponse<T = GaxiosResponseData> extends Response {
config: GaxiosOptionsPrepared;
data: T;
}

export interface GaxiosMultipartOptions {
headers: Headers;
headers: HeadersInit;
content: string | Readable;
}

/**
* Request options that are used to form the request.
*/
export interface GaxiosOptions extends Omit<RequestInit, 'headers'> {
export interface GaxiosOptions extends RequestInit {
/**
* Optional method to override making the actual HTTP request. Useful
* for writing tests.
*
* @deprecated Use {@link GaxiosOptions.fetchImplementation} instead.
*/
adapter?: <T = any>(
adapter?: <T = GaxiosResponseData>(
options: GaxiosOptionsPrepared,
defaultAdapter: (options: GaxiosOptionsPrepared) => GaxiosPromise<T>
) => GaxiosPromise<T>;
Expand All @@ -167,16 +165,6 @@ export interface GaxiosOptions extends Omit<RequestInit, 'headers'> {
| 'OPTIONS'
| 'TRACE'
| 'PATCH';
/**
* Recommended: Provide a native {@link globalThis.Headers Headers} object.
*
* @privateRemarks
*
* This type does not have the native {@link globalThis.Headers Headers} in
* its signature as it would break customers looking to modify headers before
* providing to this library (new, unnecessary type checks/guards).
*/
headers?: Headers;
/**
* The data to send in the {@link RequestInit.body} of the request. Objects will be
* serialized as JSON, except for:
Expand Down Expand Up @@ -238,7 +226,7 @@ export interface GaxiosOptions extends Omit<RequestInit, 'headers'> {
* This is passed to {@link RequestInit.body}.
*/
multipart?: GaxiosMultipartOptions[];
params?: any;
params?: GaxiosResponseData;
/**
* @deprecated Use {@link URLSearchParams} instead and pass this directly to {@link GaxiosOptions.data `data`}.
*/
Expand All @@ -247,7 +235,7 @@ export interface GaxiosOptions extends Omit<RequestInit, 'headers'> {
/**
* @deprecated ignored
*/
onUploadProgress?: (progressEvent: any) => void;
onUploadProgress?: (progressEvent: GaxiosResponseData) => void;
/**
* If the `fetchImplementation` is native `fetch`, the
* stream is a `ReadableStream`, otherwise `readable.Stream`
Expand Down Expand Up @@ -336,25 +324,6 @@ export interface GaxiosOptionsPrepared extends GaxiosOptions {
url: URL;
}

/**
* A partial object of `GaxiosOptions` with only redactable keys
*
* @experimental
*/
export type RedactableGaxiosOptions = Pick<
GaxiosOptions | GaxiosOptionsPrepared,
'body' | 'data' | 'headers' | 'url'
>;
/**
* A partial object of `GaxiosResponse` with only redactable keys
*
* @experimental
*/
export type RedactableGaxiosResponse<T = any> = Pick<
GaxiosResponse<T>,
'config' | 'data' | 'headers'
>;

/**
* Configuration for the Gaxios `request` method.
*/
Expand Down Expand Up @@ -409,7 +378,10 @@ export interface RetryConfig {
retryBackoff?: (err: GaxiosError, defaultBackoffMs: number) => Promise<void>;
}

function translateData(responseType: string | undefined, data: any) {
function translateData(
responseType: string | undefined,
data: GaxiosResponseData
) {
switch (responseType) {
case 'stream':
return data;
Expand All @@ -432,51 +404,30 @@ function translateData(responseType: string | undefined, data: any) {
*
* @experimental
*/
export function defaultErrorRedactor<T = any>(data: {
config?: RedactableGaxiosOptions;
response?: RedactableGaxiosResponse<T>;
}) {
export function defaultErrorRedactor<
O extends GaxiosOptionsPrepared,
R extends GaxiosResponse<GaxiosResponseData>,
>(data: {config?: O; response?: R}) {
const REDACT =
'<<REDACTED> - See `errorRedactor` option in `gaxios` for configuration>.';

function redactHeaders(headers?: Headers | globalThis.Headers) {
function redactHeaders(headers?: Headers) {
if (!headers) return;

function check(key: string) {
headers.forEach((_, key) => {
// any casing of `Authentication`
// any casing of `Authorization`
// anything containing secret, such as 'client secret'
return (
if (
/^authentication$/i.test(key) ||
/^authorization$/i.test(key) ||
/secret/i.test(key)
);
}

function redactHeadersObject(headers: Headers) {
for (const key of Object.keys(headers)) {
if (check(key)) headers[key] = REDACT;
}
}

function redactHeadersHeaders(headers: globalThis.Headers) {
headers.forEach((value, key) => {
if (check(key)) headers.set(key, REDACT);
});
}

// support `node-fetch` Headers and other third-parties
if (headers instanceof Headers || 'set' in headers) {
redactHeadersHeaders(headers as globalThis.Headers);
} else {
redactHeadersObject(headers);
}
)
headers.set(key, REDACT);
});
}

function redactString<T extends GaxiosOptions | RedactableGaxiosResponse>(
obj: T,
key: keyof T
) {
function redactString<T extends O | R>(obj: T, key: keyof T) {
if (
typeof obj === 'object' &&
obj !== null &&
Expand All @@ -494,9 +445,7 @@ export function defaultErrorRedactor<T = any>(data: {
}
}

function redactObject<T extends GaxiosOptions['data'] | GaxiosResponse>(
obj: T | null
) {
function redactObject<T extends O['data'] | R>(obj: T | null) {
if (!obj) {
return;
} else if (
Expand Down Expand Up @@ -535,7 +484,7 @@ export function defaultErrorRedactor<T = any>(data: {
redactObject(data.config.body);

try {
const url = new URL('', data.config.url);
const url = data.config.url;

if (url.searchParams.has('token')) {
url.searchParams.set('token', REDACT);
Expand All @@ -545,7 +494,7 @@ export function defaultErrorRedactor<T = any>(data: {
url.searchParams.set('client_secret', REDACT);
}

data.config.url = url.toString();
data.config.url = url;
} catch {
// ignore error - no need to parse an invalid URL
}
Expand Down
19 changes: 11 additions & 8 deletions src/gaxios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
GaxiosOptionsPrepared,
GaxiosPromise,
GaxiosResponse,
Headers,
defaultErrorRedactor,
} from './common';
import {getRetryConfig} from './retry';
Expand Down Expand Up @@ -210,7 +209,7 @@ export class Gaxios {

#urlMayUseProxy(
url: string | URL,
noProxy: GaxiosOptions['noProxy'] = []
noProxy: GaxiosOptionsPrepared['noProxy'] = []
): boolean {
const candidate = new URL(url);
const noProxyList = [...noProxy];
Expand Down Expand Up @@ -258,9 +257,9 @@ export class Gaxios {
* Applies the request interceptors. The request interceptors are applied after the
* call to prepareRequest is completed.
*
* @param {GaxiosOptions} options The current set of options.
* @param {GaxiosOptionsPrepared} options The current set of options.
*
* @returns {Promise<GaxiosOptions>} Promise that resolves to the set of options or response after interceptors are applied.
* @returns {Promise<GaxiosOptionsPrepared>} Promise that resolves to the set of options or response after interceptors are applied.
*/
async #applyRequestInterceptors(
options: GaxiosOptionsPrepared
Expand All @@ -283,9 +282,9 @@ export class Gaxios {
* Applies the response interceptors. The response interceptors are applied after the
* call to request is made.
*
* @param {GaxiosOptions} options The current set of options.
* @param {GaxiosOptionsPrepared} options The current set of options.
*
* @returns {Promise<GaxiosOptions>} Promise that resolves to the set of options or response after interceptors are applied.
* @returns {Promise<GaxiosOptionsPrepared>} Promise that resolves to the set of options or response after interceptors are applied.
*/
async #applyResponseInterceptors(
response: GaxiosResponse | Promise<GaxiosResponse>
Expand Down Expand Up @@ -313,7 +312,7 @@ export class Gaxios {
async #prepareRequest(
options: GaxiosOptions
): Promise<GaxiosOptionsPrepared> {
const opts: GaxiosOptions = extend(true, {}, this.defaults, options);
const opts = extend(true, {}, this.defaults, options);
if (!opts.url) {
throw new Error('URL is required.');
}
Expand Down Expand Up @@ -537,8 +536,12 @@ export class Gaxios {
) {
const finale = `--${boundary}--`;
for (const currentPart of multipartOptions) {
const headers =
currentPart.headers instanceof Headers
? currentPart.headers
: new Headers(currentPart.headers);
const partContentType =
currentPart.headers['Content-Type'] || 'application/octet-stream';
headers.get('Content-Type') || 'application/octet-stream';
const preamble = `--${boundary}\r\nContent-Type: ${partContentType}\r\n\r\n`;
yield preamble;
if (typeof currentPart.content === 'string') {
Expand Down
3 changes: 1 addition & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ export {
GaxiosError,
GaxiosPromise,
GaxiosResponse,
GaxiosOptionsPrepared as PreparedGaxiosOptions,
Headers,
GaxiosOptionsPrepared,
RetryConfig,
} from './common';
export {Gaxios, GaxiosOptions};
Expand Down
14 changes: 7 additions & 7 deletions test/test.getch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ import {
GaxiosResponse,
GaxiosPromise,
} from '../src';
import {
GAXIOS_ERROR_SYMBOL,
GaxiosOptionsPrepared,
Headers,
} from '../src/common';
import {GAXIOS_ERROR_SYMBOL, GaxiosOptionsPrepared} from '../src/common';
import {pkg} from '../src/util';
import fs from 'fs';

Expand Down Expand Up @@ -119,7 +115,11 @@ describe('🚙 error handling', () => {
bodyUsed: true,
} as GaxiosResponse;

const error = new GaxiosError('translation test', {}, response);
const error = new GaxiosError(
'translation test',
{} as GaxiosOptionsPrepared,
response
);

assert(error.response);
assert.equal(error.response.data, notJSON);
Expand All @@ -130,7 +130,7 @@ describe('🚙 error handling', () => {

const wrongVersion = {[GAXIOS_ERROR_SYMBOL]: '0.0.0'};
const correctVersion = {[GAXIOS_ERROR_SYMBOL]: pkg.version};
const child = new A('', {});
const child = new A('', {} as GaxiosOptionsPrepared);

assert.equal(wrongVersion instanceof GaxiosError, false);
assert.equal(correctVersion instanceof GaxiosError, true);
Expand Down

0 comments on commit e6c2371

Please sign in to comment.