Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Upgrade to node-fetch v3 #617

Merged
merged 28 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
58c7dc2
feat!: Upgrade to `node-fetch` v3
d-goog Apr 16, 2024
f8570d9
Merge branch 'main' into upgrade-node-fetch
danielbankhead Apr 16, 2024
7fd2a95
refactor: Use native `FormData` in testing
d-goog Apr 17, 2024
0cc7856
Merge branch 'upgrade-node-fetch' of github.com:googleapis/gaxios int…
d-goog Apr 17, 2024
a61cfa5
feat!: Improve spec compliance
d-goog Apr 19, 2024
35a73b1
test(temp): Run 18+
d-goog Apr 19, 2024
e7addeb
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Apr 19, 2024
9816229
feat: Improve types, streamline, and standardize
d-goog May 14, 2024
93bf4a8
Merge branch 'main' of github.com:googleapis/gaxios into upgrade-node…
d-goog May 15, 2024
9164b87
test: Adopt `Headers` type from merge
d-goog May 15, 2024
84faea8
feat: Introduce `GaxiosOptionsPrepared` for improved type guarantees
d-goog May 16, 2024
4e6de5f
feat: Ensure `GaxiosOptionsPrepared.url` is always a `URL`
d-goog May 16, 2024
e6c2371
refactor: Clean-up Types & Resolve lint warnings
d-goog May 16, 2024
98f7009
refactor: streamline `.data` for `Response`
d-goog May 20, 2024
4894e39
docs: Simplify example
d-goog May 22, 2024
a7e2e83
refactor: streamline, no error case
d-goog May 22, 2024
8413f68
feat: Basic `GET` Support for `Request` objects
d-goog Sep 11, 2024
04e264d
test: remove `.only`
d-goog Sep 11, 2024
d163a90
refactor: simplify `httpMethodsToRetry`
d-goog Sep 11, 2024
705fad3
chore: update `nock`
d-goog Sep 11, 2024
9e52755
test: cleanup
d-goog Sep 11, 2024
388b34c
Merge branch 'main' of github.com:googleapis/gaxios into upgrade-node…
d-goog Oct 24, 2024
a4b5b1f
fix: `File` in Node 18
d-goog Oct 24, 2024
bf17ec2
docs: clarification for node-fetch `.data`
d-goog Oct 24, 2024
24c75e1
chore: fix webpack for node-fetch v3
d-goog Oct 25, 2024
f88b1e6
docs: clarifications
d-goog Oct 25, 2024
e4c845c
fix: Types for Node.js-only environments
d-goog Oct 25, 2024
a4f98f6
Merge branch 'main' into upgrade-node-fetch
danielbankhead Oct 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: [14, 16, 18, 20]
node: [18, 20]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
Expand All @@ -32,7 +32,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 14
node-version: 18
- run: npm install
- run: npm test
env:
Expand All @@ -43,7 +43,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 14
node-version: 18
- run: npm install
- run: npm run lint
docs:
Expand All @@ -52,7 +52,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 14
node-version: 18
- run: npm install
- run: npm run docs
- uses: JustinBeckwith/linkinator-action@v1
Expand Down
17 changes: 7 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ interface GaxiosOptions = {
headers: { 'some': 'header' },

// The data to send in the body of the request. Data objects will be
// serialized as JSON.
// serialized as JSON, except for `FormData`.
//
// Note: if you would like to provide a Content-Type header other than
// application/json you you must provide a string or readable stream, rather
Expand All @@ -71,10 +71,6 @@ interface GaxiosOptions = {
// Defaults to `0`, which is the same as unset.
maxContentLength: 2000,

// The max number of HTTP redirects to follow.
// Defaults to 100.
maxRedirects: 100,

// The querystring parameters that will be encoded using `qs` and
// appended to the url
params: {
Expand Down Expand Up @@ -105,6 +101,8 @@ interface GaxiosOptions = {
// The expected return type of the request. Options are:
// json | stream | blob | arraybuffer | text | unknown
// Defaults to `unknown`.
// If the `fetchImplementation` is native `fetch`, the
// stream is a `ReadableStream`, otherwise `readable.Stream`
responseType: 'unknown',

// The node.js http agent to use for the request.
Expand All @@ -114,9 +112,9 @@ interface GaxiosOptions = {
// status code. Defaults to (>= 200 && < 300)
validateStatus: (status: number) => true,

// Implementation of `fetch` to use when making the API call. By default,
// will use the browser context if available, and fall back to `node-fetch`
// in node.js otherwise.
/**
* Implementation of `fetch` to use when making the API call. Will use `fetch` by default.
*/
fetchImplementation?: typeof fetch;

// Configuration for retrying of requests.
Expand Down Expand Up @@ -151,8 +149,7 @@ interface GaxiosOptions = {
// Enables default configuration for retries.
retry: boolean,

// Cancelling a request requires the `abort-controller` library.
// See https://github.com/bitinn/node-fetch#request-cancellation-with-abortsignal
// Enables aborting via AbortController
signal?: AbortSignal

/**
Expand Down
8 changes: 2 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,15 @@
"@types/mv": "^2.1.0",
"@types/ncp": "^2.0.1",
"@types/node": "^20.0.0",
"@types/node-fetch": "^2.5.7",
"@types/sinon": "^17.0.0",
"@types/tmp": "0.2.6",
"@types/uuid": "^9.0.0",
"abort-controller": "^3.0.0",
"assert": "^2.0.0",
"browserify": "^17.0.0",
"c8": "^8.0.0",
"cors": "^2.8.5",
"execa": "^5.0.0",
"express": "^4.16.4",
"form-data": "^4.0.0",
"gts": "^5.0.0",
"is-docker": "^2.0.0",
"karma": "^6.0.0",
Expand All @@ -74,7 +71,7 @@
"multiparty": "^4.2.1",
"mv": "^2.1.1",
"ncp": "^2.0.0",
"nock": "^13.0.0",
"nock": "^14.0.0-beta.5",
"null-loader": "^4.0.0",
"puppeteer": "^21.0.0",
"sinon": "^17.0.0",
Expand All @@ -88,8 +85,7 @@
"dependencies": {
"extend": "^3.0.2",
"https-proxy-agent": "^7.0.1",
"is-stream": "^2.0.0",
"node-fetch": "^2.6.9",
"node-fetch": "^3.3.2",
"uuid": "^9.0.1"
}
}
165 changes: 95 additions & 70 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@
try {
this.response.data = translateData(
this.config.responseType,
this.response?.data
// workaround for `node-fetch`'s `.data` deprecation...
this.response?.bodyUsed ? this.response?.data : undefined
);
} catch {
// best effort - don't throw an error within an error
Expand All @@ -118,22 +119,17 @@
}
}

/**
* @deprecated use native {@link globalThis.Headers}.
*/
export interface Headers {
[index: string]: any;

Check warning on line 126 in src/common.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
}
export type GaxiosPromise<T = any> = Promise<GaxiosResponse<T>>;

Check warning on line 128 in src/common.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

export interface GaxiosXMLHttpRequest {
responseURL: string;
}

export interface GaxiosResponse<T = any> {
export interface GaxiosResponse<T = any> extends Response {

Check warning on line 130 in src/common.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
config: GaxiosOptions;
data: T;
status: number;
statusText: string;
headers: Headers;
request: GaxiosXMLHttpRequest;
}

export interface GaxiosMultipartOptions {
Expand All @@ -144,12 +140,14 @@
/**
* Request options that are used to form the request.
*/
export interface GaxiosOptions {
export interface GaxiosOptions extends Omit<RequestInit, 'headers'> {
/**
* Optional method to override making the actual HTTP request. Useful
* for writing tests.
*
* @deprecated Use {@link GaxiosOptions.fetchImplementation} instead.
*/
adapter?: <T = any>(

Check warning on line 150 in src/common.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
options: GaxiosOptions,
defaultAdapter: (options: GaxiosOptions) => GaxiosPromise<T>
) => GaxiosPromise<T>;
Expand All @@ -169,29 +167,61 @@
| '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. Data objects will be
* serialized as JSON, except for `FormData`.
*
* Note: if you would like to provide a Content-Type header other than
* application/json you you must provide a string or readable stream, rather
* than an object:
*
* ```ts
* {data: JSON.stringify({some: 'data'})}
* {data: fs.readFile('./some-data.jpeg')}
* ```
*/
data?: any;

Check warning on line 193 in src/common.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
body?: any;
/**
* The maximum size of the http response content in bytes allowed.
* The maximum size of the http response `Content-Length` in bytes allowed.
*/
maxContentLength?: number;
/**
* The maximum number of redirects to follow. Defaults to 20.
*
* @deprecated non-spec. Should use `20` if enabled per-spec: https://fetch.spec.whatwg.org/#http-redirect-fetch
*/
maxRedirects?: number;
/**
* @deprecated non-spec. Should use `20` if enabled per-spec: https://fetch.spec.whatwg.org/#http-redirect-fetch
*/
follow?: number;
/**
* A collection of parts to send as a `Content-Type: multipart/related` request.
*
* This is passed to {@link RequestInit.body}.
*/
multipart?: GaxiosMultipartOptions[];
params?: any;

Check warning on line 214 in src/common.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
paramsSerializer?: (params: {[index: string]: string | number}) => string;
timeout?: number;
/**
* @deprecated ignored
*/
onUploadProgress?: (progressEvent: any) => void;

Check warning on line 220 in src/common.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
/**
* If the `fetchImplementation` is native `fetch`, the
* stream is a `ReadableStream`, otherwise `readable.Stream`
*/
responseType?:
| 'arraybuffer'
| 'blob'
Expand All @@ -203,16 +233,28 @@
validateStatus?: (status: number) => boolean;
retryConfig?: RetryConfig;
retry?: boolean;
// Should be instance of https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
// interface. Left as 'any' due to incompatibility between spec and abort-controller.
signal?: any;
/**
* Enables aborting via {@link AbortController}.
*/
signal?: AbortSignal;
/**
* @deprecated non-spec. https://github.com/node-fetch/node-fetch/issues/1438
*/
size?: number;
/**
* Implementation of `fetch` to use when making the API call. By default,
* will use the browser context if available, and fall back to `node-fetch`
* in node.js otherwise.
* Implementation of `fetch` to use when making the API call. Will use `fetch` by default.
*
* @example
*
* let customFetchCalled = false;
* const myFetch = (...args: Parameters<typeof fetch>) => {
* customFetchCalled = true;
* return fetch(...args);
* };
*
* {fetchImplementation: myFetch};
*/
fetchImplementation?: FetchImplementation;
fetchImplementation?: typeof fetch;
// Configure client to use mTLS:
cert?: string;
key?: string;
Expand Down Expand Up @@ -259,7 +301,7 @@
errorRedactor?: typeof defaultErrorRedactor | false;
}
/**
* A partial object of `GaxiosResponse` with only redactable keys
* A partial object of `GaxiosOptions` with only redactable keys
*
* @experimental
*/
Expand All @@ -272,7 +314,7 @@
*
* @experimental
*/
export type RedactableGaxiosResponse<T = any> = Pick<

Check warning on line 317 in src/common.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
GaxiosResponse<T>,
'config' | 'data' | 'headers'
>;
Expand Down Expand Up @@ -331,42 +373,7 @@
retryBackoff?: (err: GaxiosError, defaultBackoffMs: number) => Promise<void>;
}

export type FetchImplementation = (
input: FetchRequestInfo,
init?: FetchRequestInit
) => Promise<FetchResponse>;

export type FetchRequestInfo = any;

export interface FetchResponse {
readonly status: number;
readonly statusText: string;
readonly url: string;
readonly body: unknown | null;
arrayBuffer(): Promise<unknown>;
blob(): Promise<unknown>;
readonly headers: FetchHeaders;
json(): Promise<any>;
text(): Promise<string>;
}

export interface FetchRequestInit {
method?: string;
}

export interface FetchHeaders {
append(name: string, value: string): void;
delete(name: string): void;
get(name: string): string | null;
has(name: string): boolean;
set(name: string, value: string): void;
forEach(
callbackfn: (value: string, key: string) => void,
thisArg?: any
): void;
}

function translateData(responseType: string | undefined, data: any) {

Check warning on line 376 in src/common.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
switch (responseType) {
case 'stream':
return data;
Expand All @@ -389,32 +396,47 @@
*
* @experimental
*/
export function defaultErrorRedactor<T = any>(data: {

Check warning on line 399 in src/common.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
config?: RedactableGaxiosOptions;
response?: RedactableGaxiosResponse<T>;
}) {
const REDACT =
'<<REDACTED> - See `errorRedactor` option in `gaxios` for configuration>.';

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

for (const key of Object.keys(headers)) {
function check(key: string) {
// any casing of `Authentication`
if (/^authentication$/i.test(key)) {
headers[key] = REDACT;
}

// any casing of `Authorization`
if (/^authorization$/i.test(key)) {
headers[key] = REDACT;
}

// anything containing secret, such as 'client secret'
if (/secret/i.test(key)) {
headers[key] = REDACT;
return (
/^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);
});

// headers.g
}

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

function redactString(obj: GaxiosOptions, key: keyof GaxiosOptions) {
Expand Down Expand Up @@ -481,8 +503,11 @@
defaultErrorRedactor({config: data.response.config});
redactHeaders(data.response.headers);

redactString(data.response, 'data');
redactObject(data.response.data);
// workaround for `node-fetch`'s `.data` deprecation...
if ((data.response as {} as Response).bodyUsed) {
redactString(data.response, 'data');
redactObject(data.response.data);
}
}

return data;
Expand Down
Loading
Loading