Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ The body of the API call.

`redux-api-middleware` uses the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to make the API call. `[RSAA].body` should hence be a valid body according to the [fetch specification](https://fetch.spec.whatwg.org). In most cases, this will be a JSON-encoded string or a [`FormData`](https://developer.mozilla.org/en/docs/Web/API/FormData) object.

It may also be a function taking the state of your Redux store as its argument, and returning a body as described above.

#### `[RSAA].headers`

The HTTP headers for the API call.
Expand Down Expand Up @@ -218,7 +220,7 @@ The `[RSAA].types` property controls the output of `redux-api-middleware`. The s
- `type`: the string constant in the first position of the `[RSAA].types` array.

But errors may pop up at this stage, for several reasons:
- `redux-api-middleware` has to call those of `[RSAA].bailout`, `[RSAA].endpoint` and `[RSAA].headers` that happen to be a function, which may throw an error;
- `redux-api-middleware` has to call those of `[RSAA].bailout`, `[RSAA].endpoint`, `[RSAA].body`, `[RSAA].options` and `[RSAA].headers` that happen to be a function, which may throw an error;
- `fetch` may throw an error: the RSAA definition is not strong enough to preclude that from happening (you may, for example, send in a `[RSAA].body` that is not valid according to the fetch specification — mind the SHOULDs in the [RSAA definition](#redux-standard-api-calling-actions));
- a network failure occurs (the network is unreachable, the server responds with an error,...).

Expand Down Expand Up @@ -577,7 +579,7 @@ The `[RSAA].method` property MUST be one of the strings `GET`, `HEAD`, `POST`, `

#### `[RSAA].body`

The optional `[RSAA].body` property SHOULD be a valid body according to the [fetch specification](https://fetch.spec.whatwg.org).
The optional `[RSAA].body` property SHOULD be a valid body according to the [fetch specification](https://fetch.spec.whatwg.org), or a function. In the second case, the function SHOULD return a valid body.

#### `[RSAA].headers`

Expand Down
22 changes: 20 additions & 2 deletions src/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ function apiMiddleware({ getState }) {

// Parse the validated RSAA action
const callAPI = action[RSAA];
var { endpoint, headers, options = {} } = callAPI;
const { method, body, credentials, bailout, types } = callAPI;
var { endpoint, body, headers, options = {} } = callAPI;
const { method, credentials, bailout, types } = callAPI;
const [requestType, successType, failureType] = normalizeTypeDescriptors(
types
);
Expand Down Expand Up @@ -82,6 +82,24 @@ function apiMiddleware({ getState }) {
}
}

// Process [RSAA].body function
if (typeof body === 'function') {
try {
body = body(getState());
} catch (e) {
return next(
await actionWith(
{
...requestType,
payload: new RequestError('[RSAA].body function failed'),
error: true
},
[action, getState()]
)
);
}
}

// Process [RSAA].headers function
if (typeof headers === 'function') {
try {
Expand Down
66 changes: 66 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,48 @@ test('apiMiddleware must dispatch an error request FSA when [RSAA].bailout fails
actionHandler(anAction);
});

test('apiMiddleware must dispatch an error request FSA when [RSAA].body fails', t => {
const anAction = {
[RSAA]: {
endpoint: 'http://127.0.0.1/api/users/1',
body: () => {
throw new Error();
},
method: 'GET',
types: [
{
type: 'REQUEST',
payload: 'ignoredPayload',
meta: 'someMeta'
},
'SUCCESS',
'FAILURE'
]
}
};
const doGetState = () => {};
const nextHandler = apiMiddleware({ getState: doGetState });
const doNext = action => {
t.pass('next handler called');
t.equal(action.type, 'REQUEST', 'dispatched FSA has correct type property');
t.equal(
action.payload.message,
'[RSAA].body function failed',
'dispatched FSA has correct payload property'
);
t.equal(
action.meta,
'someMeta',
'dispatched FSA has correct meta property'
);
t.ok(action.error, 'dispatched FSA has correct error property');
};
const actionHandler = nextHandler(doNext);

t.plan(5);
actionHandler(anAction);
});

test('apiMiddleware must dispatch an error request FSA when [RSAA].endpoint fails', t => {
const anAction = {
[RSAA]: {
Expand Down Expand Up @@ -1225,6 +1267,30 @@ test('apiMiddleware must use an [RSAA].bailout function when present', t => {
actionHandler(anAction);
});

test('apiMiddleware must use an [RSAA].body function when present', t => {
const api = nock('http://127.0.0.1')
.get('/api/users/1')
.reply(200);
const anAction = {
[RSAA]: {
endpoint: 'http://127.0.0.1/api/users/1',
body: () => {
t.pass('[RSAA].body function called');
return 'body';
},
method: 'GET',
types: ['REQUEST', 'SUCCESS', 'FAILURE']
}
};
const doGetState = () => {};
const nextHandler = apiMiddleware({ getState: doGetState });
const doNext = action => {};
const actionHandler = nextHandler(doNext);

t.plan(1);
actionHandler(anAction);
});

test('apiMiddleware must use an [RSAA].endpoint function when present', t => {
const api = nock('http://127.0.0.1')
.get('/api/users/1')
Expand Down