Skip to content
This repository has been archived by the owner on Apr 4, 2022. It is now read-only.

Add fetchFunc option to client constructor #1097

Merged
merged 6 commits into from
Aug 4, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
1 change: 1 addition & 0 deletions fx-src/jsm_prefix.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/

const global = this;
const globalThis = this;

var EXPORTED_SYMBOLS = ["KintoHttpClient"];

Expand Down
7 changes: 5 additions & 2 deletions src/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
User,
Emitter,
HttpMethod,
FetchFunction,
} from "./types";
import Collection from "./collection";

Expand All @@ -47,6 +48,7 @@ export interface KintoClientOptions {
requestMode?: RequestMode;
timeout?: number;
batch?: boolean;
fetchFunc?: FetchFunction;
}

export interface PaginatedParams {
Expand Down Expand Up @@ -107,6 +109,7 @@ export default class KintoClientBase {
* @param {String} [options.bucket="default"] The default bucket to use.
* @param {String} [options.requestMode="cors"] The HTTP request mode (from ES6 fetch spec).
* @param {Number} [options.timeout=null] The request timeout in ms, if any.
* @param {Function} [options.fetchFunc=fetch] The function to be used to execute HTTP requests.
*/
constructor(remote: string, options: KintoClientOptions) {
if (typeof remote !== "string" || !remote.length) {
Expand Down Expand Up @@ -145,13 +148,13 @@ export default class KintoClientBase {

this.endpoints = endpoints;

const { requestMode, timeout } = options;
const { fetchFunc, requestMode, timeout } = options;
/**
* The HTTP instance.
* @ignore
* @type {HTTP}
*/
this.http = new HTTP(this.events, { requestMode, timeout });
this.http = new HTTP(this.events, { fetchFunc, requestMode, timeout });
this._registerHTTPEvents();
}

Expand Down
10 changes: 6 additions & 4 deletions src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { FetchResponse } from "./types";

/**
* Kinto server error code descriptors.
*/
Expand Down Expand Up @@ -45,11 +47,11 @@ class NetworkTimeoutError extends Error {

class UnparseableResponseError extends Error {
public status: number;
public response: Response;
public response: FetchResponse;
public stack?: string;
public error: Error;

constructor(response: Response, body: string, error: Error) {
constructor(response: FetchResponse, body: string, error: Error) {
const { status } = response;

super(
Expand Down Expand Up @@ -90,10 +92,10 @@ export interface ServerResponseObject {
* responses (which become UnparseableResponseErrors, above).
*/
class ServerResponse extends Error {
public response: Response;
public response: FetchResponse;
public data?: ServerResponseObject;

constructor(response: Response, json?: ServerResponseObject) {
constructor(response: FetchResponse, json?: ServerResponseObject) {
const { status } = response;
let { statusText } = response;
let errnoMsg;
Expand Down
24 changes: 16 additions & 8 deletions src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import {
UnparseableResponseError,
ServerResponseObject,
} from "./errors";
import { Emitter } from "./types";
import { Emitter, FetchFunction, FetchHeaders, FetchResponse } from "./types";

interface HttpOptions {
timeout?: number | null;
requestMode?: RequestMode;
fetchFunc?: FetchFunction;
}

interface RequestOptions {
Expand All @@ -19,7 +20,7 @@ interface RequestOptions {
export interface HttpResponse<T> {
status: number;
json: T;
headers: Headers;
headers: FetchHeaders;
}

/**
Expand Down Expand Up @@ -51,6 +52,7 @@ export default class HTTP {
public events?: Emitter;
public requestMode: RequestMode;
public timeout: number;
public fetchFunc: FetchFunction;

/**
* Constructor.
Expand Down Expand Up @@ -80,12 +82,18 @@ export default class HTTP {
* @type {Number}
*/
this.timeout = options.timeout || HTTP.defaultOptions.timeout!;

/**
* The fetch() function.
* @type {Function}
*/
this.fetchFunc = options.fetchFunc || globalThis.fetch.bind(globalThis);
}

/**
* @private
*/
timedFetch(url: string, options: RequestInit): Promise<Response> {
timedFetch(url: string, options: RequestInit): Promise<FetchResponse> {
let hasTimedout = false;
return new Promise((resolve, reject) => {
// Detect if a request has timed out.
Expand All @@ -112,7 +120,7 @@ export default class HTTP {
}
};
}
fetch(url, options)
this.fetchFunc(url, options)
.then(proceedWithHandler(resolve))
.catch(proceedWithHandler(reject));
});
Expand All @@ -121,7 +129,7 @@ export default class HTTP {
/**
* @private
*/
async processResponse<T>(response: Response): Promise<HttpResponse<T>> {
async processResponse<T>(response: FetchResponse): Promise<HttpResponse<T>> {
const { status, headers } = response;
const text = await response.text();
// Check if we have a body; if so parse it as JSON.
Expand Down Expand Up @@ -206,7 +214,7 @@ export default class HTTP {
}
}

_checkForDeprecationHeader(headers: Headers): void {
_checkForDeprecationHeader(headers: FetchHeaders): void {
const alertHeader = headers.get("Alert");
if (!alertHeader) {
return;
Expand All @@ -224,7 +232,7 @@ export default class HTTP {
}
}

_checkForBackoffHeader(headers: Headers): void {
_checkForBackoffHeader(headers: FetchHeaders): void {
let backoffMs;
const backoffHeader = headers.get("Backoff");
const backoffSeconds = backoffHeader ? parseInt(backoffHeader, 10) : 0;
Expand All @@ -238,7 +246,7 @@ export default class HTTP {
}
}

_checkForRetryAfterHeader(headers: Headers): number | undefined {
_checkForRetryAfterHeader(headers: FetchHeaders): number | undefined {
const retryAfter = headers.get("Retry-After");
if (!retryAfter) {
return;
Expand Down
19 changes: 19 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,22 @@ export interface Emitter {
on(type: string, handler: (event?: any) => void): void;
off(type: string, handler: (event?: any) => void): void;
}

export interface FetchHeaders {
keys(): IterableIterator<string> | string[];
entries(): IterableIterator<[string, string]> | [string, string][];
get(name: string): string | null;
has(name: string): boolean;
}

export interface FetchResponse {
status: number;
statusText: string;
text(): Promise<string>;
headers: FetchHeaders;
}

export type FetchFunction = (
input: RequestInfo,
init?: RequestInit | undefined
) => Promise<FetchResponse>;
Loading