clever-frontend-utils
Advanced tools
Comparing version 1.2.1 to 1.3.0
"use strict"; | ||
var __assign = (this && this.__assign) || Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
@@ -10,0 +13,0 @@ Object.defineProperty(exports, "__esModule", { value: true }); |
@@ -23,9 +23,12 @@ "use strict"; | ||
*/ | ||
var __assign = (this && this.__assign) || Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
@@ -32,0 +35,0 @@ Object.defineProperty(exports, "__esModule", { value: true }); |
@@ -17,4 +17,8 @@ export declare const newAsyncDataSelectors: (fieldPath: any, options?: { | ||
listLastUpdated: (state: any) => any; | ||
list: import("reselect").OutputSelector<any, any, (res: any) => any>; | ||
itemsByID: import("reselect").OutputSelector<any, any, (res: any) => any>; | ||
list: import("reselect").OutputSelector<any, any[], (res: any) => any[]>; | ||
itemsByID: import("reselect").OutputSelector<any, { | ||
[x: string]: any; | ||
}, (res: any) => { | ||
[x: string]: any; | ||
}>; | ||
itemLoading: (state: any, id: any) => boolean; | ||
@@ -24,4 +28,4 @@ itemLoadError: (state: any, id: any) => any; | ||
itemLastUpdated: (state: any, id: any) => any; | ||
itemsLoaded: (state: any, ids: any) => any; | ||
itemsLoaded: (state: any, ids: any) => boolean; | ||
item: (state: any, id: any) => any; | ||
}; |
@@ -27,4 +27,8 @@ "use strict"; | ||
listLastUpdated: function (state) { return listState(state).lastUpdated; }, | ||
list: reselect_1.createSelector(listData, function (data) { return _.map(data, function (value) { return value.data || options.itemDefault; }); }), | ||
itemsByID: reselect_1.createSelector(listData, function (data) { return _.mapValues(data, function (value) { return value.data || options.itemDefault; }); }), | ||
list: reselect_1.createSelector(listData, function (data) { | ||
return _.map(data, function (value) { return value.data || options.itemDefault; }); | ||
}), | ||
itemsByID: reselect_1.createSelector(listData, function (data) { | ||
return _.mapValues(data, function (value) { return value.data || options.itemDefault; }); | ||
}), | ||
itemLoading: function (state, id) { return !!itemState(state, id).isLoading; }, | ||
@@ -34,5 +38,7 @@ itemLoadError: function (state, id) { return itemState(state, id).error; }, | ||
itemLastUpdated: function (state, id) { return itemState(state, id).lastUpdated; }, | ||
itemsLoaded: function (state, ids) { return _.reduce(ids, function (loaded, id) { return loaded && itemLoaded(state, id); }, true); }, | ||
itemsLoaded: function (state, ids) { | ||
return _.reduce(ids, function (loaded, id) { return loaded && itemLoaded(state, id); }, true); | ||
}, | ||
item: function (state, id) { return itemState(state, id).data || options.itemDefault; }, | ||
}; | ||
}; |
import "isomorphic-fetch"; | ||
declare enum FetchMethod { | ||
GET = "GET", | ||
POST = "POST", | ||
PUT = "PUT", | ||
PATCH = "PATCH", | ||
DELETE = "DELETE" | ||
} | ||
export interface FetchRequest { | ||
url: string; | ||
method: string; | ||
method: FetchMethod; | ||
headers: Headers; | ||
body: string; | ||
} | ||
declare enum FetchEvent { | ||
SUCCESS = "SUCCESS", | ||
ERROR = "ERROR" | ||
} | ||
export declare type FetchEventHandler = (request: FetchRequest, response: Response) => void; | ||
declare type HeaderGenerator = (request: FetchRequest) => string; | ||
declare type GlobalHeader = string | HeaderGenerator; | ||
interface GlobalRequestOptions { | ||
cache?: RequestCache; | ||
credentials?: RequestCredentials; | ||
mode?: RequestMode; | ||
redirect?: RequestRedirect; | ||
referrerPolicy?: ReferrerPolicy; | ||
} | ||
export declare class Fetch { | ||
static Method: { | ||
GET: string; | ||
POST: string; | ||
PUT: string; | ||
PATCH: string; | ||
DELETE: string; | ||
}; | ||
static Event: { | ||
SUCCESS: string; | ||
ERROR: string; | ||
}; | ||
static _eventHandlers: { | ||
[event: string]: FetchEventHandler[]; | ||
}; | ||
static _fireEvent(event: string, request: FetchRequest, response: Response): void; | ||
static on(event: string, handler: FetchEventHandler): void; | ||
static Method: typeof FetchMethod; | ||
static Event: typeof FetchEvent; | ||
private static _eventHandlers; | ||
private static _globalHeaders; | ||
private static _globalRequestOptions; | ||
private static _fireEvent; | ||
private static _getGlobalHeadersForRequest; | ||
static reset(): void; | ||
static on(event: FetchEvent, handler: FetchEventHandler): void; | ||
static setGlobalHeader(header: string, headerValue: GlobalHeader, methods?: FetchMethod[]): void; | ||
static setGlobalOptions(options: GlobalRequestOptions): void; | ||
static get(url: any, queryParams?: {}): Promise<any>; | ||
@@ -32,3 +47,3 @@ static post(url: any, body?: {}, headers?: {}): Promise<any>; | ||
static makeRequest(url: any, options?: { | ||
method: string; | ||
method: FetchMethod; | ||
headers: {}; | ||
@@ -38,1 +53,6 @@ body: string; | ||
} | ||
export declare namespace Fetch { | ||
type Method = FetchMethod; | ||
type Event = FetchEvent; | ||
} | ||
export {}; |
"use strict"; | ||
var __assign = (this && this.__assign) || Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
@@ -45,5 +48,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
}; | ||
var _a; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
require("isomorphic-fetch"); | ||
var _ = require("lodash"); | ||
var FetchMethod; | ||
(function (FetchMethod) { | ||
FetchMethod["GET"] = "GET"; | ||
FetchMethod["POST"] = "POST"; | ||
FetchMethod["PUT"] = "PUT"; | ||
FetchMethod["PATCH"] = "PATCH"; | ||
FetchMethod["DELETE"] = "DELETE"; | ||
})(FetchMethod || (FetchMethod = {})); | ||
var ALL_METHODS = Object.values(FetchMethod); | ||
var FetchEvent; | ||
(function (FetchEvent) { | ||
FetchEvent["SUCCESS"] = "SUCCESS"; | ||
FetchEvent["ERROR"] = "ERROR"; | ||
})(FetchEvent || (FetchEvent = {})); | ||
var Fetch = /** @class */ (function () { | ||
@@ -57,4 +75,26 @@ function Fetch() { | ||
// We use setTimeout to push each operation into the background. | ||
this._eventHandlers[event].forEach(function (handler) { return setTimeout(function () { return handler(request, response.clone()); }, 1); }); | ||
this._eventHandlers[event].forEach(function (handler) { | ||
return setTimeout(function () { return handler(request, response.clone()); }, 1); | ||
}); | ||
}; | ||
Fetch._getGlobalHeadersForRequest = function (request) { | ||
var headers = {}; | ||
var globalHeadersForMethod = this._globalHeaders[request.method]; | ||
Object.keys(globalHeadersForMethod).forEach(function (key) { | ||
var value = globalHeadersForMethod[key]; | ||
var headerValue = _.isFunction(value) ? value(request) : value; | ||
// Don't include headers that are empty, null, or undefined. | ||
if (headerValue) { | ||
headers[key] = headerValue; | ||
} | ||
}); | ||
return headers; | ||
}; | ||
// Resets Fetch's internal state and any custom configurations. | ||
Fetch.reset = function () { | ||
var _this = this; | ||
this._eventHandlers = {}; | ||
ALL_METHODS.forEach(function (method) { return (_this._globalHeaders[method] = {}); }); | ||
this._globalRequestOptions = {}; | ||
}; | ||
/* Registers an event handler for the specified event. Multiple event handlers can be | ||
@@ -68,2 +108,16 @@ added to the same event and they all will trigger. */ | ||
}; | ||
/* Sets a header to be included with all requests of the specified methods. A global header can | ||
be a string, in which case it's constant, or a function that takes the request as input and | ||
returns a value to use for the header. If the function returns a falsey value the header isn't included. */ | ||
Fetch.setGlobalHeader = function (header, headerValue, methods) { | ||
var _this = this; | ||
if (methods === void 0) { methods = ALL_METHODS; } | ||
methods.forEach(function (method) { | ||
_this._globalHeaders[method][header] = headerValue; | ||
}); | ||
}; | ||
/* Sets global options to be used with all requests. */ | ||
Fetch.setGlobalOptions = function (options) { | ||
this._globalRequestOptions = options; | ||
}; | ||
Fetch.get = function (url, queryParams) { | ||
@@ -96,3 +150,3 @@ if (queryParams === void 0) { queryParams = {}; } | ||
case 0: return [4 /*yield*/, Fetch.makeRequest(url, { | ||
method: Fetch.Method.POST, | ||
method: FetchMethod.POST, | ||
headers: __assign({}, headers, { "Content-Type": "application/json" }), | ||
@@ -113,3 +167,3 @@ body: JSON.stringify(body), | ||
case 0: return [4 /*yield*/, Fetch.makeRequest(url, { | ||
method: Fetch.Method.PUT, | ||
method: FetchMethod.PUT, | ||
headers: __assign({}, headers, { "Content-Type": "application/json" }), | ||
@@ -130,3 +184,3 @@ body: JSON.stringify(body), | ||
case 0: return [4 /*yield*/, Fetch.makeRequest(url, { | ||
method: Fetch.Method.PATCH, | ||
method: FetchMethod.PATCH, | ||
headers: __assign({}, headers, { "Content-Type": "application/json" }), | ||
@@ -145,3 +199,3 @@ body: JSON.stringify(body), | ||
case 0: return [4 /*yield*/, Fetch.makeRequest(url, { | ||
method: Fetch.Method.DELETE, | ||
method: FetchMethod.DELETE, | ||
headers: {}, | ||
@@ -157,5 +211,5 @@ body: undefined, | ||
Fetch.makeRequest = function (url, options) { | ||
if (options === void 0) { options = { method: Fetch.Method.GET, headers: {}, body: "" }; } | ||
if (options === void 0) { options = { method: FetchMethod.GET, headers: {}, body: "" }; } | ||
return __awaiter(this, void 0, void 0, function () { | ||
var method, headers, body, eventRequest, response, err_1, responseBody, error, extendedError; | ||
var method, headers, body, eventRequest, headersWithGlobals, response, err_1, responseBody, error, extendedError; | ||
return __generator(this, function (_a) { | ||
@@ -166,12 +220,11 @@ switch (_a.label) { | ||
eventRequest = { url: url, headers: new Headers(headers), method: method, body: body }; | ||
headersWithGlobals = new Headers(__assign({}, this._getGlobalHeadersForRequest(eventRequest), options.headers)); | ||
// Update the headers on eventRequest to be the actual headers we'll use for the request (including global headers). | ||
eventRequest.headers = headersWithGlobals; | ||
_a.label = 1; | ||
case 1: | ||
_a.trys.push([1, 3, , 4]); | ||
return [4 /*yield*/, fetch(url, { | ||
method: method, | ||
headers: new Headers(headers), | ||
credentials: "same-origin", | ||
return [4 /*yield*/, fetch(url, __assign({ method: method, headers: headersWithGlobals, credentials: "same-origin", | ||
// Firefox and Safari throw an error if a body is provided for a GET request | ||
body: (method === Fetch.Method.GET) ? undefined : body, | ||
})]; | ||
body: method === FetchMethod.GET ? undefined : body }, this._globalRequestOptions))]; | ||
case 2: | ||
@@ -183,3 +236,6 @@ response = _a.sent(); | ||
throw new Error("Fetch internal error: " + err_1.message); | ||
case 4: return [4 /*yield*/, response.clone().json().catch(function () { return response.clone().text(); })]; | ||
case 4: return [4 /*yield*/, response | ||
.clone() | ||
.json() | ||
.catch(function () { return response.clone().text(); })]; | ||
case 5: | ||
@@ -190,6 +246,6 @@ responseBody = _a.sent(); | ||
extendedError = __assign({}, error, { status: response.status, body: responseBody }); | ||
this._fireEvent(Fetch.Event.ERROR, eventRequest, response); | ||
this._fireEvent(FetchEvent.ERROR, eventRequest, response); | ||
throw extendedError; | ||
} | ||
this._fireEvent(Fetch.Event.SUCCESS, eventRequest, response); | ||
this._fireEvent(FetchEvent.SUCCESS, eventRequest, response); | ||
return [2 /*return*/, responseBody]; | ||
@@ -200,17 +256,17 @@ } | ||
}; | ||
// Method and Event should really be enums, but eslint doesn't recognize enums unfortunately. | ||
Fetch.Method = { | ||
GET: "GET", | ||
POST: "POST", | ||
PUT: "PUT", | ||
PATCH: "PATCH", | ||
DELETE: "DELETE", | ||
}; | ||
Fetch.Event = { | ||
SUCCESS: "SUCCESS", | ||
ERROR: "ERROR", | ||
}; | ||
// These static fields are used to allow FetchMethod and FetchEvent to be | ||
// accessible through Fetch.Method and Fetch.Event rather than exporting them. | ||
Fetch.Method = FetchMethod; | ||
Fetch.Event = FetchEvent; | ||
Fetch._eventHandlers = {}; | ||
Fetch._globalHeaders = (_a = {}, | ||
_a[FetchMethod.GET] = {}, | ||
_a[FetchMethod.POST] = {}, | ||
_a[FetchMethod.PUT] = {}, | ||
_a[FetchMethod.PATCH] = {}, | ||
_a[FetchMethod.DELETE] = {}, | ||
_a); | ||
Fetch._globalRequestOptions = {}; | ||
return Fetch; | ||
}()); | ||
exports.Fetch = Fetch; |
@@ -1,2 +0,2 @@ | ||
export const asPending = (action) => ({ | ||
export const asPending = action => ({ | ||
...action, | ||
@@ -9,5 +9,5 @@ meta: { | ||
export const asError = (action) => ({ | ||
export const asError = action => ({ | ||
...action, | ||
error: true, | ||
}); |
@@ -107,3 +107,3 @@ /** | ||
// Leave existing data intact but update metadata if necessary | ||
const newState = newAsyncDataReducer((currentState) => currentState || {})(state, action); | ||
const newState = newAsyncDataReducer(currentState => currentState || {})(state, action); | ||
@@ -132,3 +132,6 @@ if (newState.isLoading || newState.error) { | ||
const currentItemState = _.get(state, `data.${itemKey}`) || {}; | ||
const newItemState = newAsyncDataReducer(itemReduceFn)(currentItemState, { ...action, payload: item }); | ||
const newItemState = newAsyncDataReducer(itemReduceFn)(currentItemState, { | ||
...action, | ||
payload: item, | ||
}); | ||
newState.data[itemKey] = newItemState; | ||
@@ -135,0 +138,0 @@ } |
@@ -5,10 +5,10 @@ import { createSelector as createMemoizedSelector } from "reselect"; | ||
export const newAsyncDataSelectors = (fieldPath, options = { itemDefault: null }) => { | ||
const itemState = (state) => _.get(state, fieldPath, {}); | ||
const itemState = state => _.get(state, fieldPath, {}); | ||
return { | ||
itemLoading: (state) => !!itemState(state).isLoading, | ||
itemLoadError: (state) => itemState(state).error, | ||
itemLoaded: (state) => !!itemState(state).lastUpdated, | ||
itemLastUpdated: (state) => itemState(state).lastUpdated, | ||
item: (state) => itemState(state).data || options.itemDefault, | ||
itemLoading: state => !!itemState(state).isLoading, | ||
itemLoadError: state => itemState(state).error, | ||
itemLoaded: state => !!itemState(state).lastUpdated, | ||
itemLastUpdated: state => itemState(state).lastUpdated, | ||
item: state => itemState(state).data || options.itemDefault, | ||
}; | ||
@@ -18,4 +18,4 @@ }; | ||
export const newAsyncListSelectors = (subKeyPath, options = { itemDefault: null }) => { | ||
const listState = (state) => _.get(state, subKeyPath, {}); | ||
const listData = (state) => listState(state).data || {}; | ||
const listState = state => _.get(state, subKeyPath, {}); | ||
const listData = state => listState(state).data || {}; | ||
const itemState = (state, id) => listData(state)[id] || {}; | ||
@@ -25,13 +25,11 @@ const itemLoaded = (state, id) => !!itemState(state, id).lastUpdated; | ||
return { | ||
listLoading: (state) => !!listState(state).isLoading, | ||
listLoadError: (state) => listState(state).error, | ||
listLoaded: (state) => !!listState(state).lastUpdated, | ||
listLastUpdated: (state) => listState(state).lastUpdated, | ||
list: createMemoizedSelector( | ||
listData, | ||
(data) => _.map(data, (value) => value.data || options.itemDefault), | ||
listLoading: state => !!listState(state).isLoading, | ||
listLoadError: state => listState(state).error, | ||
listLoaded: state => !!listState(state).lastUpdated, | ||
listLastUpdated: state => listState(state).lastUpdated, | ||
list: createMemoizedSelector(listData, data => | ||
_.map(data, value => value.data || options.itemDefault), | ||
), | ||
itemsByID: createMemoizedSelector( | ||
listData, | ||
(data) => _.mapValues(data, (value) => value.data || options.itemDefault), | ||
itemsByID: createMemoizedSelector(listData, data => | ||
_.mapValues(data, value => value.data || options.itemDefault), | ||
), | ||
@@ -43,9 +41,6 @@ | ||
itemLastUpdated: (state, id) => itemState(state, id).lastUpdated, | ||
itemsLoaded: (state, ids) => _.reduce( | ||
ids, | ||
(loaded, id) => loaded && itemLoaded(state, id), | ||
true, | ||
), | ||
itemsLoaded: (state, ids) => | ||
_.reduce(ids, (loaded, id) => loaded && itemLoaded(state, id), true), | ||
item: (state, id) => itemState(state, id).data || options.itemDefault, | ||
}; | ||
}; |
import "isomorphic-fetch"; | ||
import * as _ from "lodash"; | ||
enum FetchMethod { | ||
GET = "GET", | ||
POST = "POST", | ||
PUT = "PUT", | ||
PATCH = "PATCH", | ||
DELETE = "DELETE", | ||
} | ||
const ALL_METHODS: Fetch.Method[] = Object.values(FetchMethod); | ||
export interface FetchRequest { | ||
url: string; | ||
method: string; | ||
method: FetchMethod; | ||
headers: Headers; | ||
@@ -11,22 +21,41 @@ body: string; | ||
enum FetchEvent { | ||
SUCCESS = "SUCCESS", | ||
ERROR = "ERROR", | ||
} | ||
export type FetchEventHandler = (request: FetchRequest, response: Response) => void; | ||
type HeaderGenerator = (request: FetchRequest) => string; | ||
type GlobalHeader = string | HeaderGenerator; | ||
// A subset of RequestInit that we allow setting as global options that | ||
// get used with each request. | ||
interface GlobalRequestOptions { | ||
cache?: RequestCache; | ||
credentials?: RequestCredentials; | ||
mode?: RequestMode; | ||
redirect?: RequestRedirect; | ||
referrerPolicy?: ReferrerPolicy; | ||
} | ||
export class Fetch { | ||
// Method and Event should really be enums, but eslint doesn't recognize enums unfortunately. | ||
static Method = { | ||
GET: "GET", | ||
POST: "POST", | ||
PUT: "PUT", | ||
PATCH: "PATCH", | ||
DELETE: "DELETE", | ||
}; | ||
// These static fields are used to allow FetchMethod and FetchEvent to be | ||
// accessible through Fetch.Method and Fetch.Event rather than exporting them. | ||
static Method = FetchMethod; | ||
static Event = FetchEvent; | ||
static Event = { | ||
SUCCESS: "SUCCESS", | ||
ERROR: "ERROR", | ||
private static _eventHandlers: { [event in FetchEvent]?: FetchEventHandler[] } = {}; | ||
private static _globalHeaders: { | ||
[method in Fetch.Method]: { [header: string]: GlobalHeader }; | ||
} = { | ||
[FetchMethod.GET]: {}, | ||
[FetchMethod.POST]: {}, | ||
[FetchMethod.PUT]: {}, | ||
[FetchMethod.PATCH]: {}, | ||
[FetchMethod.DELETE]: {}, | ||
}; | ||
private static _globalRequestOptions: GlobalRequestOptions = {}; | ||
static _eventHandlers: { [event: string]: FetchEventHandler[] } = {}; | ||
static _fireEvent(event: string, request: FetchRequest, response: Response) { | ||
private static _fireEvent(event: FetchEvent, request: FetchRequest, response: Response) { | ||
if (!this._eventHandlers[event]) { | ||
@@ -36,10 +65,31 @@ return; | ||
// We use setTimeout to push each operation into the background. | ||
this._eventHandlers[event].forEach( | ||
handler => setTimeout(() => handler(request, response.clone()), 1) | ||
this._eventHandlers[event].forEach(handler => | ||
setTimeout(() => handler(request, response.clone()), 1), | ||
); | ||
} | ||
private static _getGlobalHeadersForRequest(request: FetchRequest): { [header: string]: string } { | ||
const headers = {}; | ||
const globalHeadersForMethod = this._globalHeaders[request.method]; | ||
Object.keys(globalHeadersForMethod).forEach(key => { | ||
const value = globalHeadersForMethod[key]; | ||
const headerValue = _.isFunction(value) ? (value as HeaderGenerator)(request) : value; | ||
// Don't include headers that are empty, null, or undefined. | ||
if (headerValue) { | ||
headers[key] = headerValue; | ||
} | ||
}); | ||
return headers; | ||
} | ||
// Resets Fetch's internal state and any custom configurations. | ||
static reset() { | ||
this._eventHandlers = {}; | ||
ALL_METHODS.forEach(method => (this._globalHeaders[method] = {})); | ||
this._globalRequestOptions = {}; | ||
} | ||
/* Registers an event handler for the specified event. Multiple event handlers can be | ||
added to the same event and they all will trigger. */ | ||
static on(event: string, handler: FetchEventHandler) { | ||
static on(event: FetchEvent, handler: FetchEventHandler) { | ||
if (!this._eventHandlers[event]) { | ||
@@ -51,6 +101,20 @@ this._eventHandlers[event] = []; | ||
/* Sets a header to be included with all requests of the specified methods. A global header can | ||
be a string, in which case it's constant, or a function that takes the request as input and | ||
returns a value to use for the header. If the function returns a falsey value the header isn't included. */ | ||
static setGlobalHeader(header: string, headerValue: GlobalHeader, methods = ALL_METHODS) { | ||
methods.forEach(method => { | ||
this._globalHeaders[method][header] = headerValue; | ||
}); | ||
} | ||
/* Sets global options to be used with all requests. */ | ||
static setGlobalOptions(options: GlobalRequestOptions) { | ||
this._globalRequestOptions = options; | ||
} | ||
static async get(url, queryParams = {}) { | ||
let queryString = ""; | ||
if (!_.isEmpty(queryParams)) { | ||
const paramStrings = Object.keys(queryParams).map((key) => `${key}=${queryParams[key]}`); | ||
const paramStrings = Object.keys(queryParams).map(key => `${key}=${queryParams[key]}`); | ||
queryString = `?${paramStrings.join("&")}`; | ||
@@ -65,3 +129,3 @@ } | ||
return await Fetch.makeRequest(url, { | ||
method: Fetch.Method.POST, | ||
method: FetchMethod.POST, | ||
headers: { | ||
@@ -77,3 +141,3 @@ ...headers, | ||
return await Fetch.makeRequest(url, { | ||
method: Fetch.Method.PUT, | ||
method: FetchMethod.PUT, | ||
headers: { | ||
@@ -89,3 +153,3 @@ ...headers, | ||
return await Fetch.makeRequest(url, { | ||
method: Fetch.Method.PATCH, | ||
method: FetchMethod.PATCH, | ||
headers: { | ||
@@ -101,3 +165,3 @@ ...headers, | ||
return await Fetch.makeRequest(url, { | ||
method: Fetch.Method.DELETE, | ||
method: FetchMethod.DELETE, | ||
headers: {}, | ||
@@ -109,15 +173,24 @@ body: undefined, | ||
// Fetch's underlying method | ||
static async makeRequest(url, options = { method: Fetch.Method.GET, headers: {}, body: "" }) { | ||
static async makeRequest(url, options = { method: FetchMethod.GET, headers: {}, body: "" }) { | ||
const { method, headers, body } = options; | ||
// Initialize eventRequest with the explicitly specified headers so we can use it in _getGlobalHeadersForRequest. | ||
const eventRequest = { url, headers: new Headers(headers), method, body }; | ||
const headersWithGlobals = new Headers({ | ||
...this._getGlobalHeadersForRequest(eventRequest), | ||
...options.headers, | ||
}); | ||
// Update the headers on eventRequest to be the actual headers we'll use for the request (including global headers). | ||
eventRequest.headers = headersWithGlobals; | ||
let response; | ||
let response: Response; | ||
try { | ||
response = await fetch(url, { | ||
method, | ||
headers: new Headers(headers), | ||
headers: headersWithGlobals, | ||
credentials: "same-origin", | ||
// Firefox and Safari throw an error if a body is provided for a GET request | ||
body: (method === Fetch.Method.GET) ? undefined : body, | ||
body: method === FetchMethod.GET ? undefined : body, | ||
// Apply global request options for the request. | ||
...this._globalRequestOptions, | ||
}); | ||
@@ -130,3 +203,6 @@ } catch (err) { | ||
// because responses can only be read once | ||
const responseBody = await response.clone().json().catch(() => response.clone().text()); | ||
const responseBody = await response | ||
.clone() | ||
.json() | ||
.catch(() => response.clone().text()); | ||
@@ -140,8 +216,16 @@ if (!response.ok) { | ||
}; | ||
this._fireEvent(Fetch.Event.ERROR, eventRequest, response); | ||
this._fireEvent(FetchEvent.ERROR, eventRequest, response); | ||
throw extendedError; | ||
} | ||
this._fireEvent(Fetch.Event.SUCCESS, eventRequest, response); | ||
this._fireEvent(FetchEvent.SUCCESS, eventRequest, response); | ||
return responseBody; | ||
} | ||
} | ||
// eslint-disable-next-line no-redeclare | ||
export namespace Fetch { | ||
// The static fields on the Fetch class allow using the enums, but | ||
// doesn't allow using the type. Exporting the types on the Fetch namespace does the trick. | ||
export type Method = FetchMethod; | ||
export type Event = FetchEvent; | ||
} |
{ | ||
"name": "clever-frontend-utils", | ||
"version": "1.2.1", | ||
"version": "1.3.0", | ||
"description": "A set of utils for frontend projects at Clever (and potentially elsewhere!)", | ||
@@ -24,12 +24,17 @@ "main": "dist/index.js", | ||
"@types/express": "^4.16.1", | ||
"@types/fetch-mock": "^7.3.0", | ||
"@types/jest": "^18.1.1", | ||
"@types/lodash": "^4.14.134", | ||
"@types/node": "^10.12.0", | ||
"@typescript-eslint/eslint-plugin": "^1.9.0", | ||
"@typescript-eslint/parser": "^1.9.0", | ||
"babel-eslint": "^7.1.1", | ||
"eslint": "3.14.1", | ||
"eslint-config-airbnb": "^5.0.1", | ||
"eslint-plugin-react": "^3.16.1", | ||
"eslint": "5.x", | ||
"eslint-config-airbnb": "^6.2.0", | ||
"eslint-plugin-react": "^4.2.3", | ||
"fetch-mock": "^7.3.3", | ||
"jest": "^18.1.0", | ||
"prettier": "1.18.2", | ||
"ts-jest": "^18.0.3", | ||
"tslint": "^3.15.1", | ||
"typescript": "^2.1.5" | ||
"typescript": "3.x" | ||
}, | ||
@@ -36,0 +41,0 @@ "dependencies": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
107753
45
2296
16