Skip to content

Commit

Permalink
feat(request): allow passing custom fetch function
Browse files Browse the repository at this point in the history
allows passing a custom fetch fuction in IRequestOptions, also fixes some tests and removes peer
dpes

AFFECTS PACKAGES:
@esri/rest-auth
@esri/rest-request
  • Loading branch information
patrickarlt committed Sep 13, 2017
1 parent d56faee commit 920d1ff
Show file tree
Hide file tree
Showing 13 changed files with 110 additions and 111 deletions.
2 changes: 1 addition & 1 deletion jasmine.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"spec_dir": "packages",
"spec_files": ["*/{src,test}/**/*.test.ts"],
"helpers": ["../support/register-tsnode.js"],
"helpers": ["../support/test-helpers.js"],
"stopSpecOnExpectationFailure": false,
"random": false
}
11 changes: 0 additions & 11 deletions packages/rest-auth/package-lock.json

This file was deleted.

2 changes: 2 additions & 0 deletions packages/rest-auth/test/ApplicationSession.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import * as fetchMock from "fetch-mock";
import { YESTERDAY, TOMORROW } from "./utils";

describe("ApplicationSession", () => {
afterEach(fetchMock.restore);

describe(".getToken()", () => {
it("should return the cached token if it is not expired", done => {
const session = new ApplicationSession({
Expand Down
6 changes: 4 additions & 2 deletions packages/rest-auth/test/fetchToken.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe("fetchToken()", () => {

afterEach(fetchMock.restore);

it("should request a token with `client_credentials`, `client_id` and `client_secret`", () => {
it("should request a token with `client_credentials`, `client_id` and `client_secret`", done => {
fetchMock.postOnce(TOKEN_URL, {
access_token: "token",
expires_in: 1800
Expand All @@ -39,13 +39,14 @@ describe("fetchToken()", () => {
);
expect(response.token).toEqual("token");
expect(response.expires).toBeGreaterThan(Date.now());
done();
})
.catch(e => {
fail(e);
});
});

it("should request a token with `authorization_code`, `client_id` and `redirect_uri`", () => {
it("should request a token with `authorization_code`, `client_id` and `redirect_uri`", done => {
fetchMock.postOnce(TOKEN_URL, {
access_token: "token",
expires_in: 1800,
Expand Down Expand Up @@ -78,6 +79,7 @@ describe("fetchToken()", () => {
expect(response.refreshToken).toEqual("refreshToken");
expect(response.username).toEqual("Casey");
expect(response.expires).toBeGreaterThan(Date.now());
done();
})
.catch(e => {
fail(e);
Expand Down
3 changes: 2 additions & 1 deletion packages/rest-auth/test/generateToken.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe("generateToken()", () => {

afterEach(fetchMock.restore);

it("should generate a token for a username and password", () => {
it("should generate a token for a username and password", done => {
fetchMock.postOnce(TOKEN_URL, {
token: "token",
expires: TOMORROW.getTime()
Expand All @@ -35,6 +35,7 @@ describe("generateToken()", () => {
expect(paramsSpy).toHaveBeenCalledWith("password", "Jones");
expect(response.token).toEqual("token");
expect(response.expires).toEqual(TOMORROW.getTime());
done();
})
.catch(e => {
fail(e);
Expand Down
11 changes: 1 addition & 10 deletions packages/rest-request/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,7 @@
"dependencies": {
"tslib": "^1.7.1"
},
"peerDependencies": {
"es6-promise": "^4.1.1",
"isomorphic-fetch": "^2.2.1",
"isomorphic-form-data": "^1.0.0"
},
"devDependencies": {
"es6-promise": "^4.1.1",
"isomorphic-fetch": "^2.2.1",
"isomorphic-form-data": "^1.0.0"
},
"devDependencies": {},
"scripts": {
"prepublish": "npm run build",
"build": "npm run build:node && npm run build:umd && npm run build:esm",
Expand Down
1 change: 0 additions & 1 deletion packages/rest-request/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// bump, bump
export * from "./request";
export * from "./utils/encode-form-data";
export * from "./utils/encode-query-string";
Expand Down
101 changes: 48 additions & 53 deletions packages/rest-request/src/request.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import "es6-promise/auto";
import "isomorphic-fetch";
import { checkForErrors } from "./utils/check-for-errors";
import { encodeFormData, FormData } from "./utils/encode-form-data";
import { encodeFormData } from "./utils/encode-form-data";
import { encodeQueryString } from "./utils/encode-query-string";

export { FormData };

export interface IAuthenticationManager {
getToken(url: string): Promise<string>;
}
Expand Down Expand Up @@ -44,6 +40,11 @@ export interface IRequestOptions {
* The instance of `IAuthenticationManager` to use to authenticate this request.
*/
authentication?: IAuthenticationManager;

/**
* The implimentation of `fetch` to use. Defaults to a global `fetch`
*/
fetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>;
}

/**
Expand Down Expand Up @@ -89,7 +90,7 @@ export function request(
requestOptions?: IRequestOptions
): Promise<any> {
const options: IRequestOptions = {
...{ httpMethod: "POST" },
...{ httpMethod: "POST", fetch },
...requestOptions
};

Expand All @@ -104,51 +105,45 @@ export function request(
method: httpMethod
};

const tokenRequest = authentication
? authentication.getToken(url)
: Promise.resolve("");

return tokenRequest.then(token => {
if (token.length) {
params.token = token;
}

if (httpMethod === "GET") {
url = url + "?" + encodeQueryString(params);
}

if (httpMethod === "POST") {
fetchOptions.body = encodeFormData(params);
}

return fetch(url, fetchOptions)
.then(response => {
switch (params.f) {
case "json":
return response.json();
case "geojson":
return response.json();
/* istanbul ignore next blob responses are difficult to make cross platform we will just have to trust the isomorphic fetch will do its job */
case "image":
return response.blob();
case "html":
return response.text();
case "text":
return response.text();
/* istanbul ignore next blob responses are difficult to make cross platform we will just have to trust the isomorphic fetch will do its job */
case "image":
return response.blob();
/* istanbul ignore next blob responses are difficult to make cross platform we will just have to trust the isomorphic fetch will do its job */
case "zip":
return response.blob();
}
})
.then(data => {
if (params.f === "json" || params.f === "geojson") {
return checkForErrors(data, url, params, options);
} else {
return data;
}
});
});
return (authentication ? authentication.getToken(url) : Promise.resolve(""))
.then(token => {
if (token.length) {
params.token = token;
}

if (httpMethod === "GET") {
url = url + "?" + encodeQueryString(params);
}

if (httpMethod === "POST") {
fetchOptions.body = encodeFormData(params);
}

return options.fetch(url, fetchOptions);
})
.then(response => {
switch (params.f) {
case "json":
return response.json();
case "geojson":
return response.json();
case "html":
return response.text();
case "text":
return response.text();
/* istanbul ignore next blob responses are difficult to make cross platform we will just have to trust the isomorphic fetch will do its job */
case "image":
return response.blob();
/* istanbul ignore next blob responses are difficult to make cross platform we will just have to trust the isomorphic fetch will do its job */
case "zip":
return response.blob();
}
})
.then(data => {
if (params.f === "json" || params.f === "geojson") {
return checkForErrors(data, url, params, options);
} else {
return data;
}
});
}
3 changes: 0 additions & 3 deletions packages/rest-request/src/utils/encode-form-data.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as FormData from "isomorphic-form-data";
import { processParams } from "./process-params";

/**
Expand All @@ -15,5 +14,3 @@ export function encodeFormData(params: any): FormData {
});
return formData;
}

export { FormData };
7 changes: 1 addition & 6 deletions packages/rest-request/test/ArcGISAuthError.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
FormData,
ArcGISAuthError,
IRetryAuthError,
ErrorTypes
} from "../src/index";
import { ArcGISAuthError, IRetryAuthError, ErrorTypes } from "../src/index";
import { ArcGISOnlineAuthError, ArcGISOnlineError } from "./mocks/errors";
import * as fetchMock from "fetch-mock";

Expand Down
50 changes: 43 additions & 7 deletions packages/rest-request/test/request.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { request, FormData, ErrorTypes } from "../src/index";
import { request, ErrorTypes } from "../src/index";
import * as fetchMock from "fetch-mock";
import {
SharingRestInfo,
Expand All @@ -21,7 +21,7 @@ describe("request()", () => {

afterEach(fetchMock.restore);

it("should make a basic POST request", () => {
it("should make a basic POST request", done => {
fetchMock.once("*", SharingRestInfo);

request("https://www.arcgis.com/sharing/rest/info")
Expand All @@ -31,13 +31,14 @@ describe("request()", () => {
expect(options.method).toBe("POST");
expect(paramsSpy).toHaveBeenCalledWith("f", "json");
expect(response).toEqual(SharingRestInfo);
done();
})
.catch(e => {
fail(e);
});
});

it("should make a basic GET request", () => {
it("should make a basic GET request", done => {
fetchMock.once("*", SharingRestInfo);

request(
Expand All @@ -52,6 +53,7 @@ describe("request()", () => {
expect(url).toEqual("https://www.arcgis.com/sharing/rest/info?f=json");
expect(options.method).toBe("GET");
expect(response).toEqual(SharingRestInfo);
done();
})
.catch(e => {
fail(e);
Expand Down Expand Up @@ -104,7 +106,7 @@ describe("request()", () => {
});
});

it("should make a basic GET request for geojson", () => {
it("should make a basic GET request for geojson", done => {
fetchMock.once("*", GeoJSONFeatureCollection);

request(
Expand All @@ -121,13 +123,14 @@ describe("request()", () => {
);
expect(options.method).toBe("GET");
expect(response).toEqual(GeoJSONFeatureCollection);
done();
})
.catch(e => {
fail(e);
});
});

it("should use the `authentication` option to authenticate a request", () => {
it("should use the `authentication` option to authenticate a request", done => {
fetchMock.once("*", WebMapAsText);

const MOCK_AUTH = {
Expand All @@ -149,8 +152,8 @@ describe("request()", () => {
"https://www.arcgis.com/sharing/rest/content/items/43a8e51789044d9480a20089a84129ad/data"
);
expect(paramsSpy).toHaveBeenCalledWith("token", "token");

expect(response).toEqual(WebMapAsJSON);
done();
})
.catch(e => {
fail(e);
Expand All @@ -170,8 +173,41 @@ describe("request()", () => {
"https://www.arcgis.com/sharing/rest/content/items/43a8e51789044d9480a20089a84129ad/data"
);
expect(error.params).toEqual({ f: "json" });
expect(error.options).toEqual({ httpMethod: "POST" });
expect(error.options).toEqual({ httpMethod: "POST", fetch });
done();
});
});

it("should allow you to use custom implimentations of `fetch`", done => {
const MockFetchResponse = {
json() {
return Promise.resolve(SharingRestInfo);
},
blob() {
return Promise.resolve(new Blob([JSON.stringify(SharingRestInfo)]));
},
text() {
return Promise.resolve(JSON.stringify(SharingRestInfo));
}
};

const MockFetch = function() {
return Promise.resolve(MockFetchResponse);
};

request(
"https://www.arcgis.com/sharing/rest/info",
{},
{
fetch: MockFetch as any
}
)
.then(response => {
expect(response).toEqual(SharingRestInfo);
done();
})
.catch(e => {
fail(e);
});
});
});
Loading

0 comments on commit 920d1ff

Please sign in to comment.