Skip to content

Commit

Permalink
Merge pull request #77 from oberonamsterdam/feature/add-parse-method
Browse files Browse the repository at this point in the history
Add Body interface methods (blob, arrayBuffer etc.) as optional parse method (default is still json)
  • Loading branch information
SpreadTriad authored Jun 24, 2021
2 parents e211e43 + 910bdf0 commit b2daec2
Show file tree
Hide file tree
Showing 13 changed files with 112 additions and 34 deletions.
18 changes: 18 additions & 0 deletions api.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
- [`ConfigAfterProps`](#configafterprops)
- [`State`](#state)
- [`RequestHandler`](#requesthandler)
- [`RequestConfig`](#requestconfig)
- [`HandledResponse`](#handledresponse)
- [`Options`](#options)

Expand Down Expand Up @@ -471,6 +472,9 @@ Specification and configuration of an endpoint.
Provide default params for the params included in the url.
- `enableSuspense?`: **boolean**
Enables [React Suspense](https://reactjs.org/docs/concurrent-mode-suspense.html) for the endpoint.
- `parseMethod?`: **'json' | 'blob' | 'text' | 'arrayBuffer' | 'formData'**
Parse responses automatically as json, text or any of the other options.
Defaults to 'json'.

---

Expand All @@ -489,6 +493,9 @@ Global configuration for all endpoints.
- `timeout?` **number**
- `enableSuspense?`: **boolean**
Enables [React Suspense](https://reactjs.org/docs/concurrent-mode-suspense.html) globally.
- `parseMethod?`: **'json' | 'blob' | 'text' | 'arrayBuffer' | 'formData'**
Parse responses automatically as json, text or any of the other options.
Defaults to 'json'.

---

Expand Down Expand Up @@ -532,13 +539,24 @@ Type: **Function**
- `url` **string**
- `requestProperties` **[RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)**
The `init` parameter for fetch.
- `config?` **[RequestConfig](#requestconfig)**
Extra config options for handling the fetch request

**Returns**

**Promise<[HandledResponse](#handledresponse)>**

---

### `RequestConfig`

**Properties**

- `parseMethod?`: **'json' | 'blob' | 'text' | 'arrayBuffer' | 'formData'**
Parse response automatically as json, text or any of the other options.

---

### `HandledResponse`

**Properties**
Expand Down
3 changes: 1 addition & 2 deletions flow/actions/performRequest.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// @flow

import type { EndpointParams } from '../types';
import type { EndpointParams, RequestHandler } from '../types';
import type { ActionCreator } from 'redux';
import type { Action, State } from '../reducer';
import type { RequestHandler } from '../request';

declare export var performRequest: (
endpointKey: string,
Expand Down
2 changes: 2 additions & 0 deletions flow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export type {
ConfigAfterProps,
Binding,
Actions,
ParseMethod,
RequestConfig,
} from './types';

export {
Expand Down
9 changes: 1 addition & 8 deletions flow/request.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
// @flow

export interface HandledResponse {
response: Response;
body: any;
}
import type { RequestHandler } from './types';

export type RequestHandler = (
url: string,
requestProperties?: RequestOptions
) => Promise<HandledResponse>;
declare var defaultRequestHandler: RequestHandler;
declare export default typeof defaultRequestHandler;
29 changes: 28 additions & 1 deletion flow/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,14 @@ export interface GlobalConfig {
afterFailed?: (afterProps: ConfigAfterProps) => void;
timeout?: number;
autoTrigger?: boolean;
/*
* Enable React suspense for all endpoints
*/
enableSuspense?: boolean;
/*
* Parse response as json, text, blob, formData or arrayBuffer, defaults to json
*/
parseMethod?: ParseMethod;
}

export type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
Expand Down Expand Up @@ -99,7 +106,14 @@ export interface EndpointConfig {

timeout?: number;
autoTrigger?: boolean;
/*
* Enable React suspense for this endpoint
*/
enableSuspense?: boolean;
/*
* Parse response as json, text, blob, formData or arrayBuffer, defaults to json
*/
parseMethod?: ParseMethod;
}

export interface ConfigBeforeProps {
Expand Down Expand Up @@ -151,4 +165,17 @@ export type Options = {
isSSR?: boolean,
}

export type HookOptions = $Shape<EndpointConfig> | Options;
export type HookOptions = $Shape<EndpointConfig> | Options;

export type ParseMethod = 'json' | 'blob' | 'text' | 'arrayBuffer' | 'formData';

export interface RequestConfig {
parseMethod?: ParseMethod;
}

export interface HandledResponse {
response: Response;
body: any;
}

export type RequestHandler = (url: string, requestProperties?: RequestOptions, config?: RequestConfig) => Promise<HandledResponse>;
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-api-data",
"version": "1.1.3",
"version": "1.2.0",
"description": "Fetch and normalize data from api",
"main": "lib/index.js",
"scripts": {
Expand Down
4 changes: 2 additions & 2 deletions src/actions/performRequest.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import getState from '../mocks/mockState';
import { performRequest } from './performRequest';
import request, { HandledResponse } from '../request';
import request from '../request';
import { success } from './success';
import { getRequestKey } from '../helpers/getRequestKey';
import { fail } from './fail';
import { ConfigBeforeProps, EndpointParams } from '../types';
import { ConfigBeforeProps, EndpointParams, HandledResponse } from '../types';
import { getRequest, getResultData } from '..';
import thunk from 'redux-thunk';
import { applyMiddleware, combineReducers, createStore } from 'redux';
Expand Down
21 changes: 17 additions & 4 deletions src/actions/performRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@ import {
GlobalConfig,
EndpointParams,
DataRequest,
HandledResponse,
RequestConfig,
RequestHandler,
} from '../types';
import { getRequest } from '../selectors/getRequest';
import { fail } from './fail';
import { success } from './success';
import { getRequestKey } from '../helpers/getRequestKey';
import { formatUrl } from '../helpers/formatUrl';
import { BindingsStore } from '../helpers/createBinding';
import Request, { HandledResponse } from '../request';
import Request from '../request';
import { cacheExpired } from '../selectors/cacheExpired';
import { RequestHandler } from '../request';
import { getActions } from '../helpers/getActions';
import { Dispatch } from 'redux';
import { getFailedData } from '../selectors/getFailedData';
Expand All @@ -28,7 +30,7 @@ export const getRequestProperties = (
globalConfig: GlobalConfig,
state: any,
body?: any
) => {
): RequestInit => {
const defaultProperties = { body, headers: {}, method: endpointConfig.method };
const requestProperties = composeConfigPipeFn(
endpointConfig.setRequestProperties,
Expand All @@ -42,6 +44,16 @@ export const getRequestProperties = (
return requestProperties;
};

export const getRequestConfig = (
endpointConfig: EndpointConfig,
globalConfig: GlobalConfig,
): RequestConfig => {
const parseMethod = endpointConfig.parseMethod ?? globalConfig.parseMethod ?? 'json';
return {
parseMethod
};
};

// passes return value from endpoint function to global function
const composeConfigPipeFn = (endpointFn?: any, globalFunction?: any): any => {
const id = (val: any) => val;
Expand Down Expand Up @@ -143,6 +155,7 @@ export const performRequest: PerformRequest = (
});

const requestProperties = getRequestProperties(config, globalConfig, state, body);
const requestConfig = getRequestConfig(config, globalConfig);
const promise = new Promise((resolve: (binding: Binding<any, any>) => void, reject: (binding: Binding<any, any>) => void) => {
const timeout = config.timeout || globalConfig.timeout;
let abortTimeout: any;
Expand All @@ -154,7 +167,7 @@ export const performRequest: PerformRequest = (
handleFail(new Error('Timeout'));
}, timeout);
}
requestFunction(url, requestProperties).then(
requestFunction(url, requestProperties, requestConfig).then(
(handledResponse: HandledResponse) => {
if (aborted) {
return;
Expand Down
5 changes: 3 additions & 2 deletions src/getDataFromTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ type RenderFn = (tree: ReactNode) => string;
const getDataFromTree = (
tree: ReactNode,
store: Store<{ apiData: State }>,
renderFn: RenderFn = require('react-dom/server').renderToStaticMarkup,
renderFn?: RenderFn,
) => {
renderFn(tree);
const renderFunction = renderFn ?? require('react-dom/server').renderToStaticMarkup;
renderFunction(tree);
const { apiData } = store.getState();
return Promise.all(Object.keys(apiData.requests).map((requestKey: string) => {
const promise = getLoadingPromise(requestKey);
Expand Down
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import {
ConfigAfterProps,
Binding,
Actions,
ParseMethod,
RequestConfig,
} from './types';

export {
Expand Down Expand Up @@ -55,6 +57,8 @@ export {
ConfigAfterProps,
Binding,
Actions,
ParseMethod,
RequestConfig,
};

/**
Expand Down
15 changes: 5 additions & 10 deletions src/request.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
const __DEV__ = process.env.NODE_ENV === 'development';

export interface HandledResponse {
response: Response;
body: any;
}
import { RequestHandler } from './types';

export type RequestHandler = (url: string, requestProperties?: RequestInit) => Promise<HandledResponse>;
const __DEV__ = process.env.NODE_ENV === 'development';

/*
* Get the headers based on request properties, adds:
Expand Down Expand Up @@ -47,7 +42,7 @@ const getHeaders = (requestProperties: RequestInit): HeadersInit => {
* connection fails.
*/

const defaultRequestHandler: RequestHandler = (url, requestProperties = {}) => {
const defaultRequestHandler: RequestHandler = (url, requestProperties = {}, { parseMethod = 'json' } = { }) => {
if (__DEV__) {
console.log('Executing request: ' + url);
}
Expand All @@ -70,15 +65,15 @@ const defaultRequestHandler: RequestHandler = (url, requestProperties = {}) => {
body: {},
});
} else {
response.json().then(
response[parseMethod]().then(
(body: any) =>
resolve({
response,
body,
}),
err => {
if (__DEV__) {
console.warn(`Could not parse JSON response of ${url}`);
console.warn(`Could not parse ${parseMethod} response of ${url}`);
}
resolve({
response,
Expand Down
32 changes: 29 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,14 @@ export interface GlobalConfig {
afterFailed?: (afterProps: ConfigAfterProps) => void;
timeout?: number;
autoTrigger?: boolean;
/*
* Enable React suspense for all endpoints
*/
enableSuspense?: boolean;
/*
* Parse response as json, text, blob, formData or arrayBuffer, defaults to json
*/
parseMethod?: ParseMethod;
}

/**
Expand Down Expand Up @@ -109,19 +116,26 @@ export interface EndpointConfig {
*/
setHeaders?: (defaultHeaders: object, state: object) => object;
/*
* defaultPropertie will be the properties returned by the setRequestproperties function from the global config, if set
* defaultProperties will be the properties returned by the setRequestproperties function from the global config, if set
*/
setRequestProperties?: (defaultProperties: object, state: object) => object;
/*
* Set defaultParams in a URL.
* Set default params in a URL.
*/
defaultParams?: {
[paramName: string]: string | number;
};

timeout?: number;
autoTrigger?: boolean;
/*
* Enable React suspense for this endpoint
*/
enableSuspense?: boolean;
/*
* Parse response as json, text, blob, formData or arrayBuffer, defaults to json
*/
parseMethod?: ParseMethod;
}

export interface ConfigBeforeProps {
Expand Down Expand Up @@ -177,4 +191,16 @@ export interface Options {
isSSR?: boolean;
}

export interface HookOptions extends Partial<EndpointConfig>, Options {}
export interface HookOptions extends Partial<EndpointConfig>, Options {}

export interface HandledResponse {
response: Response;
body: any;
}

export type ParseMethod = 'json' | 'blob' | 'text' | 'arrayBuffer' | 'formData';

export interface RequestConfig {
parseMethod?: ParseMethod;
}
export type RequestHandler = (url: string, requestProperties?: RequestInit, config?: RequestConfig) => Promise<HandledResponse>;

0 comments on commit b2daec2

Please sign in to comment.