Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

redux-api-middleware

Package Overview
Dependencies
Maintainers
1
Versions
42
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

redux-api-middleware - npm Package Compare versions

Comparing version 0.5.0 to 0.6.0

lib/ApiError.js

214

lib/index.js

@@ -7,19 +7,6 @@ /**

* @exports {Symbol} CALL_API
* @exports {function} isRSAA
* @exports {ReduxMiddleWare} apiMiddleware
*/
/**
* @typedef {function} ReduxMiddleware
* @param {Object} store
* @returns {ReduxNextHandler}
*
* @typedef {function} ReduxNextHandler
* @param {function} next
* @returns {ReduxActionHandler}
*
* @typedef {function} ReduxActionHandler
* @param {object} action
* @returns undefined
*/
'use strict';

@@ -29,198 +16,23 @@

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
exports.isRSAA = isRSAA;
exports.apiMiddleware = apiMiddleware;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var _CALL_API = require('./CALL_API');
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _CALL_API2 = _interopRequireDefault(_CALL_API);
var _normalizr = require('normalizr');
var _validateRSAA = require('./validateRSAA');
var _isomorphicFetch = require('isomorphic-fetch');
var _validateRSAA2 = _interopRequireDefault(_validateRSAA);
var _isomorphicFetch2 = _interopRequireDefault(_isomorphicFetch);
var _isRSAA = require('./isRSAA');
var _lodashIsplainobject = require('lodash.isplainobject');
var _isRSAA2 = _interopRequireDefault(_isRSAA);
var _lodashIsplainobject2 = _interopRequireDefault(_lodashIsplainobject);
var _apiMiddleware = require('./apiMiddleware');
/**
* Error class for an API response outside the 200 range
*
* @class ApiError
* @access private
* @param {number} status - the status code of the API response
* @param {string} statusText - the status text of the API response
* @param {Object} response - the JSON response of the API server if the 'Content-Type'
* header signals a JSON response, or the raw response object otherwise
*/
var _apiMiddleware2 = _interopRequireDefault(_apiMiddleware);
var ApiError = (function (_Error) {
_inherits(ApiError, _Error);
function ApiError(status, statusText, response) {
_classCallCheck(this, ApiError);
_Error.call(this);
this.name = 'ApiError';
this.status = status;
this.statusText = statusText;
this.message = status + ' - ' + statusText;
this.response = response;
}
/**
* Fetches an API response and normalizes the resulting JSON according to schema.
*
* @function callApi
* @access private
* @param {string} endpoint - The URL endpoint for the request
* @param {string} method - The HTTP method for the request
* @param {boolean} [auth=false] - Whether to send authentication credentials or not
* @param {Object} [body] - The body of the request
* @param {Schema} [schema] - The normalizr schema with which to parse the response
* @returns {Promise}
*/
return ApiError;
})(Error);
function callApi(endpoint, method, headers, body, schema) {
var requestOptions = { method: method, body: body, headers: headers };
return _isomorphicFetch2['default'](endpoint, requestOptions).then(function (response) {
if (response.ok) {
return Promise.resolve(response);
} else {
return Promise.reject(response);
}
}).then(function (response) {
var contentType = response.headers.get('Content-Type');
if (contentType && ~contentType.indexOf('json')) {
return response.json().then(function (json) {
if (schema) {
return Promise.resolve(_normalizr.normalize(json, schema));
} else {
return Promise.resolve(json);
}
});
} else {
return Promise.resolve();
}
}, function (response) {
var contentType = response.headers.get('Content-Type');
if (contentType && ~contentType.indexOf('json')) {
return response.json().then(function (json) {
return Promise.reject(new ApiError(response.status, response.statusText, json));
});
} else {
return Promise.reject(new ApiError(response.status, response.statusText, response));
}
});
}
/**
* Symbol key that carries API call info interpreted by this Redux middleware.
*
* @constant {Symbol}
* @access public
* @default
*/
var CALL_API = Symbol('Call API');
exports.CALL_API = CALL_API;
/**
* Is the given action a Redux Standard API-calling action?
*
* @function isRSAA
* @access public
* @param {Object} action - The action to check against the RSAA definition.
* @returns {boolean}
*/
function isRSAA(action) {
var validRootKeys = [[CALL_API], 'payload', 'meta'];
var validCallAPIKeys = ['endpoint', 'method', 'types', 'body', 'headers', 'schema', 'bailout'];
var validMethods = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
var callAPI = action[CALL_API];
if (!_lodashIsplainobject2['default'](action) || typeof callAPI === 'undefined') {
return false;
}
var endpoint = callAPI.endpoint;
var method = callAPI.method;
var body = callAPI.body;
var headers = callAPI.headers;
var schema = callAPI.schema;
var types = callAPI.types;
var bailout = callAPI.bailout;
return Object.keys(action).every(function (key) {
return ~validRootKeys.indexOf(key);
}) && _lodashIsplainobject2['default'](callAPI) && Object.keys(callAPI).every(function (key) {
return ~validCallAPIKeys.indexOf(key);
}) && (typeof endpoint === 'string' || typeof endpoint === 'function') && ~validMethods.indexOf(method.toUpperCase()) && (Array.isArray(types) && types.length === 3) && (typeof headers === 'undefined' || _lodashIsplainobject2['default'](headers)) && (typeof schema === 'undefined' || schema instanceof _normalizr.Schema || schema.hasOwnProperty('_itemSchema')) && (typeof bailout === 'undefined' || typeof bailout === 'boolean' || typeof bailout === 'function');
}
/**
* A Redux middleware that interprets actions with CALL_API info specified.
* Performs the call and promises when such actions are dispatched.
*
* @type {ReduxMiddleware}
* @access public
*/
function apiMiddleware(_ref) {
var getState = _ref.getState;
return function (next) {
return function (action) {
var callAPI = action[CALL_API];
if (!isRSAA(action)) {
return next(action);
}
var endpoint = callAPI.endpoint;
var method = callAPI.method;
var body = callAPI.body;
var headers = callAPI.headers;
var schema = callAPI.schema;
var types = callAPI.types;
var bailout = callAPI.bailout;
if (typeof endpoint === 'function') {
endpoint = endpoint(getState());
}
if (typeof bailout === 'boolean' && bailout || typeof bailout === 'function' && bailout(getState())) {
return Promise.resolve('Bailing out');
}
function actionWith(data, payload) {
var finalPayload = _extends({}, action.payload, payload);
var finalAction = _extends({}, action, { payload: finalPayload }, data);
delete finalAction[CALL_API];
return finalAction;
}
var requestType = types[0];
var successType = types[1];
var failureType = types[2];
next(actionWith({ type: requestType }));
return callApi(endpoint, method, headers, body, schema).then(function (response) {
return next(actionWith({ type: successType }, response));
}, function (error) {
return next(actionWith({
type: failureType,
payload: error,
error: true
}));
});
};
};
}
exports.CALL_API = _CALL_API2['default'];
exports.validateRSAA = _validateRSAA2['default'];
exports.isRSAA = _isRSAA2['default'];
exports.apiMiddleware = _apiMiddleware2['default'];
{
"name": "redux-api-middleware",
"version": "0.5.0",
"version": "0.6.0",
"description": "Redux middleware for calling an API.",

@@ -30,15 +30,15 @@ "main": "lib/index.js",

"isomorphic-fetch": "^2.1.1",
"lodash.isplainobject": "^3.2.0",
"normalizr": "^1.0.0"
"lodash.isplainobject": "^3.2.0"
},
"devDependencies": {
"babel": "^5.8.21",
"babel-istanbul": "^0.3.17",
"babel": "^5.8.23",
"babel-istanbul": "^0.3.20",
"coveralls": "^2.11.4",
"eslint": "^1.1.0",
"nock": "^2.10.0",
"rimraf": "^2.4.2",
"tap-spec": "^4.0.2",
"eslint": "^1.5.1",
"nock": "^2.13.0",
"normalizr": "^1.0.0",
"rimraf": "^2.4.3",
"tap-spec": "^4.1.0",
"tape": "^4.2.0"
}
}

@@ -82,3 +82,3 @@ redux-api-middleware

- have a `headers` property,
- have a `schema` property,
- have a `transform` property,
- have a `bailout` property.

@@ -88,3 +88,3 @@

- include properties other than `endpoint`, `method`, `types`, `body`, `headers`, `schema` and `bailout`.
- include properties other than `endpoint`, `method`, `types`, `body`, `headers`, `transform` and `bailout`.

@@ -97,3 +97,3 @@ ### `[CALL_API].endpoint`

The `[CALL_API].method` property MUST be one of the strings `GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE` or `OPTIONS (in any mixture of lowercase and uppercase letters). It represents the HTTP method for the API request.
The `[CALL_API].method` property MUST be one of the strings `GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE` or `OPTIONS` (in any mixture of lowercase and uppercase letters). It represents the HTTP method for the API request.

@@ -112,5 +112,5 @@ ### `[CALL_API].types`

### `[CALL_API].schema`
### `[CALL_API].transform`
The optional `[CALL_API].schema` property MUST be a [`normalizr`](https://www.npmjs.com/package/normalizr) schema, or an `arrayOf` thereof. It specifies with which `normalizr` schema we should process the API response
The optional `[CALL_API].transform` property MUST be a function. It is used to process the JSON response of a successful API request.

@@ -137,3 +137,3 @@ ### `[CALL_API].bailout`

- If the request is successful, an FSA with the `SUCCESS` type is dispatched to the next middleware.
- The `payload` property of this FSA is a merge of the original RSAA's `payload` property and the JSON response from the server.
- The `payload` property of this FSA is a merge of the original RSAA's `payload` property and the JSON response from the server (optionally processed by `[CALL_API].transform`).
- The `meta` property of this FSA is that of the original RSAA.

@@ -157,3 +157,3 @@ - If the request is unsuccessful, an FSA with the `FAILURE` type is dispatched to the next middleware.

import { CALL_API } from 'redux-api-middleware';
import { Schema } from 'normalizr';
import { Schema, normalize } from 'normalizr';

@@ -169,3 +169,3 @@ const userSchema = new Schema({...});

headers: { credentials: 'same-origin'},
schema
transform: (response) => normalize(json, schema)
},

@@ -215,3 +215,3 @@ payload: { somePayload },

type: 'FETCH_USER.SUCCESS',
payload: { ...somePayload, response },
payload: { ...somePayload, transformedResponse },
meta: { someMeta }

@@ -232,7 +232,11 @@ }

Apart from the middleware above, `redux-api-middleware` exposes the following utility function.
Apart from the middleware above (and the `[CALL_API]` Symbol), `redux-api-middleware` exposes the following utility functions.
### validateRSAA(action)
Validates `action` against the RSAA definition, returning an array of validation errors.
### isRSAA(action)
Returns `true` if `action` is RSAA-compliant.
Returns `true` if `action` is RSAA-compliant, and `false` otherwise. Internally, it simply checks the length of the array of validation errors returned by `validateRSAA(action)`.

@@ -251,2 +255,27 @@ ## Installation

## Upgrading from <0.6.0
In previous versions, `[CALL_API]` had an optional `schema` key that would use a [`normalizr`](https://www.npmjs.com/package/normalizr) schema (or an `arrayOf` thereof) to process the JSON response of a successful API request. This has been now replaced with the optional `transform` key, so you can do any post-processing you like.
Upgrading is easy: just rewrite your RSAA-actions
```js
{
[CALL_API]: {
...
schema = someSchema,
...
}
}
```
as follows:
```js
{
[CALL_API]: {
...
transform = (response) => normalize(response, someSchema),
...
}
```
(and remember to import `normalizr`).
## License

@@ -253,0 +282,0 @@

@@ -7,196 +7,12 @@ /**

* @exports {Symbol} CALL_API
* @exports {function} isRSAA
* @exports {ReduxMiddleWare} apiMiddleware
*/
/**
* @typedef {function} ReduxMiddleware
* @param {Object} store
* @returns {ReduxNextHandler}
*
* @typedef {function} ReduxNextHandler
* @param {function} next
* @returns {ReduxActionHandler}
*
* @typedef {function} ReduxActionHandler
* @param {object} action
* @returns undefined
*/
import { normalize, Schema } from 'normalizr';
import fetch from 'isomorphic-fetch';
import isPlainObject from 'lodash.isplainobject';
import CALL_API from './CALL_API';
import validateRSAA from './validateRSAA';
import isRSAA from './isRSAA';
import apiMiddleware from './apiMiddleware';
/**
* Error class for an API response outside the 200 range
*
* @class ApiError
* @access private
* @param {number} status - the status code of the API response
* @param {string} statusText - the status text of the API response
* @param {Object} response - the JSON response of the API server if the 'Content-Type'
* header signals a JSON response, or the raw response object otherwise
*/
class ApiError extends Error {
constructor(status, statusText, response) {
super();
this.name = 'ApiError';
this.status = status;
this.statusText = statusText;
this.message = `${status} - ${statusText}`;
this.response = response;
}
}
/**
* Fetches an API response and normalizes the resulting JSON according to schema.
*
* @function callApi
* @access private
* @param {string} endpoint - The URL endpoint for the request
* @param {string} method - The HTTP method for the request
* @param {boolean} [auth=false] - Whether to send authentication credentials or not
* @param {Object} [body] - The body of the request
* @param {Schema} [schema] - The normalizr schema with which to parse the response
* @returns {Promise}
*/
function callApi(endpoint, method, headers, body, schema) {
const requestOptions = { method, body, headers }
return fetch(endpoint, requestOptions)
.then((response) => {
if (response.ok) {
return Promise.resolve(response);
} else {
return Promise.reject(response);
}
})
.then((response) => {
const contentType = response.headers.get('Content-Type');
if (contentType && ~contentType.indexOf('json')) {
return response.json().then((json) => {
if (schema) {
return Promise.resolve(normalize(json, schema));
} else {
return Promise.resolve(json);
}
});
} else {
return Promise.resolve();
}
},
(response) => {
const contentType = response.headers.get('Content-Type');
if (contentType && ~contentType.indexOf('json')) {
return response.json().then((json) => {
return Promise.reject(new ApiError(response.status, response.statusText, json));
});
} else {
return Promise.reject(new ApiError(response.status, response.statusText, response));
}
});
}
/**
* Symbol key that carries API call info interpreted by this Redux middleware.
*
* @constant {Symbol}
* @access public
* @default
*/
export const CALL_API = Symbol('Call API');
/**
* Is the given action a Redux Standard API-calling action?
*
* @function isRSAA
* @access public
* @param {Object} action - The action to check against the RSAA definition.
* @returns {boolean}
*/
export function isRSAA(action) {
const validRootKeys = [
[CALL_API],
'payload',
'meta'
];
const validCallAPIKeys = [
'endpoint',
'method',
'types',
'body',
'headers',
'schema',
'bailout'
];
const validMethods = [
'GET',
'HEAD',
'POST',
'PUT',
'PATCH',
'DELETE',
'OPTIONS'
]
const callAPI = action[CALL_API];
if (!isPlainObject(action) || typeof callAPI === 'undefined') {
return false;
}
const { endpoint, method, body, headers, schema, types, bailout } = callAPI;
return Object.keys(action).every((key) => ~validRootKeys.indexOf(key)) &&
isPlainObject(callAPI) &&
Object.keys(callAPI).every((key) => ~validCallAPIKeys.indexOf(key)) &&
(typeof endpoint === 'string' || typeof endpoint === 'function') &&
~validMethods.indexOf(method.toUpperCase()) &&
(Array.isArray(types) && types.length === 3) &&
(typeof headers === 'undefined' || isPlainObject(headers)) &&
(typeof schema === 'undefined' || schema instanceof Schema || schema.hasOwnProperty('_itemSchema')) &&
(typeof bailout === 'undefined' || typeof bailout === 'boolean' || typeof bailout === 'function');
}
/**
* A Redux middleware that interprets actions with CALL_API info specified.
* Performs the call and promises when such actions are dispatched.
*
* @type {ReduxMiddleware}
* @access public
*/
export function apiMiddleware({ getState }) {
return (next) => (action) => {
const callAPI = action[CALL_API];
if (!isRSAA(action)) {
return next(action);
}
let { endpoint } = callAPI;
const { method, body, headers, schema, types, bailout } = callAPI;
if (typeof endpoint === 'function') {
endpoint = endpoint(getState());
}
if ((typeof bailout === 'boolean' && bailout) ||
(typeof bailout === 'function' && bailout(getState()))) {
return Promise.resolve('Bailing out');
}
function actionWith(data, payload) {
const finalPayload = { ...action.payload, ...payload };
const finalAction = { ...action, payload: finalPayload, ...data };
delete finalAction[CALL_API];
return finalAction;
}
const [requestType, successType, failureType] = types;
next(actionWith({ type: requestType }));
return callApi(endpoint, method, headers, body, schema).then(
(response) => next(actionWith({ type: successType }, response)),
(error) => next(actionWith({
type: failureType,
payload: error,
error: true
}))
);
};
}
export { CALL_API, validateRSAA, isRSAA, apiMiddleware };
import test from 'tape';
import { Schema, arrayOf } from 'normalizr';
import { Schema, normalize, arrayOf } from 'normalizr';
import nock from 'nock';
import { CALL_API, apiMiddleware, isRSAA } from '../src';
import { CALL_API, apiMiddleware, validateRSAA, isRSAA } from '../src';
test('isRSAA must identify RSAA-compliant actions', function (t) {
t.notOk(isRSAA(''), 'RSAA actions must be plain JavaScript objects');
t.notOk(isRSAA({}), 'RSAA actions must have an [API_CALL] property');
t.notOk(isRSAA({ invalidKey: '' }), 'RSAA actions must not have properties other than [API_CALL], payload and meta');
t.notOk(isRSAA({
test('validateRSAA/isRSAA must identify RSAA-compliant actions', function (t) {
var action1 = '';
t.ok(
validateRSAA(action1).length === 1 &&
validateRSAA(action1).includes('RSAA must be a plain JavaScript object'),
'RSAA actions must be plain JavaScript objects (validateRSAA)'
);
t.notOk(
isRSAA(action1),
'RSAA actions must be plain JavaScript objects (isRSAA)'
);
var action2 = {};
t.ok(
validateRSAA(action2).length === 1 &&
validateRSAA(action2).includes('Missing [CALL_API] key'),
'RSAA actions must have a [CALL_API] property (validateRSAA)'
);
t.notOk(
isRSAA(action2),
'RSAA actions must have a [CALL_API] property (isRSAA)'
);
var action3 = {
[CALL_API]: {},
invalidKey: ''
};
t.ok(
validateRSAA(action3).length === 1 &&
validateRSAA(action3).includes('Invalid root key: invalidKey'),
'RSAA actions must not have properties other than [CALL_API], payload and meta (validateRSAA)'
);
t.notOk(
isRSAA(action3),
'RSAA actions must not have properties other than [CALL_API], payload and meta (isRSAA)'
);
var action4 = {
[CALL_API]: ''
}), '[CALL_API] must be a plain JavaScript object');
t.notOk(isRSAA({
};
t.ok(
validateRSAA(action4).length === 1 &&
validateRSAA(action4).includes('[CALL_API] property must be a plain JavaScript object'),
'[CALL_API] must be a plain JavaScript object (validateRSAA)'
);
t.notOk(
isRSAA(action4),
'[CALL_API] must be a plain JavaScript object (isRSAA)'
);
var action5 = {
[CALL_API]: { invalidKey: '' }
}), '[CALL_API] must not have properties other than endpoint, method, body, headers, schema, types and bailout');
t.notOk(isRSAA({
};
t.ok(
validateRSAA(action5).length === 1 &&
validateRSAA(action5).includes('Invalid [CALL_API] key: invalidKey'),
'[CALL_API] must not have properties other than endpoint, method, body, headers, schema, types and bailout (validateRSAA)'
);
t.notOk(
isRSAA(action5),
'[CALL_API] must not have properties other than endpoint, method, body, headers, schema, types and bailout (isRSAA)'
);
var action6 = {
[CALL_API]: {}
}), '[CALL_API] must have an endpoint property');
t.notOk(isRSAA({
};
t.ok(
validateRSAA(action6).length === 3 &&
validateRSAA(action6).includes(
'[CALL_API].endpoint property must be a string or a function',
'[CALL_API].method property must be a string',
'[CALL_API].types property must be an array of length 3'
),
'[CALL_API] must have endpoint, method and types properties (validateRSAA)'
);
t.notOk(
isRSAA(action6),
'[CALL_API] must have endpoint, method and types properties (isRSAA)'
);
var action7 = {
[CALL_API]: {
endpoint: {}
endpoint: {},
method: 'GET',
types: ['REQUEST', 'SUCCESS', 'FAILURE']
}
}), '[CALL_API].endpoint must be a string or a function');
t.notOk(isRSAA({
};
t.ok(
validateRSAA(action7).includes('[CALL_API].endpoint property must be a string or a function'),
'[CALL_API].endpoint must be a string or a function (validateRSAA)'
);
t.notOk(
isRSAA(action7),
'[CALL_API].endpoint must be a string or a function (isRSAA)'
);
var action8 = {
[CALL_API]: {
endpoint: '',
method: ''
method: 'InvalidMethod',
types: ['REQUEST', 'SUCCESS', 'FAILURE']
}
}), '[CALL_API].method must be one of the strings \'GET\', \'HEAD\', \'POST\', \'PUT\', \'PATCH\' \'DELETE\' or \'OPTIONS\'');
t.notOk(isRSAA({
};
t.ok(
validateRSAA(action8).includes('Invalid [CALL_API].method: INVALIDMETHOD'),
'[CALL_API].method must be one of the strings \'GET\', \'HEAD\', \'POST\', \'PUT\', \'PATCH\' \'DELETE\' or \'OPTIONS\' (validateRSAA)'
);
t.notOk(
isRSAA(action8),
'[CALL_API].method must be one of the strings \'GET\', \'HEAD\', \'POST\', \'PUT\', \'PATCH\' \'DELETE\' or \'OPTIONS\' (isRSAA)'
);
var action9 = {
[CALL_API]: {
endpoint: '',
method: 'GET',
types: ''
types: {}
}
}), '[CALL_API].types must be an array');
t.notOk(isRSAA({
};
t.ok(
validateRSAA(action9).includes('[CALL_API].types property must be an array of length 3'),
'[CALL_API].types must be an array (validateRSAA)'
);
t.notOk(
isRSAA(action9),
'[CALL_API].types must be an array (isRSAA)'
);
var action10 = {
[CALL_API]: {
endpoint: '',
method: 'GET',
types: ['', '']
types: ['a', 'b']
}
}), '[CALL_API].types must have length 3');
t.notOk(isRSAA({
};
t.ok(
validateRSAA(action10).includes('[CALL_API].types property must be an array of length 3'),
'[CALL_API].types must have length 3 (validateRSAA)'
);
t.notOk(
isRSAA(action10),
'[CALL_API].types must have length 3 (isRSAA)'
);
var action11 = {
[CALL_API]: {

@@ -50,7 +156,15 @@ endpoint: '',

types: ['REQUEST', 'SUCCESS', 'FAILURE'],
body: {},
headers: ''
}
}), '[CALL_API].headers must be a plain JavaScript object');
t.notOk(isRSAA({
};
t.ok(
validateRSAA(action11).includes('[CALL_API].headers property must be undefined, or a plain JavaScript object'),
'[CALL_API].headers must be undefined, or a plain JavaScript object (validateRSAA)'
);
t.notOk(
isRSAA(action11),
'[CALL_API].headers must be undefined, or a plain JavaScript object (isRSAA)'
);
var action12 = {
[CALL_API]: {

@@ -60,8 +174,15 @@ endpoint: '',

types: ['REQUEST', 'SUCCESS', 'FAILURE'],
body: {},
headers: {},
schema: ''
transform: ''
}
}), '[CALL_API].schema must be a normalizr schema');
t.notOk(isRSAA({
};
t.ok(
validateRSAA(action12).includes('[CALL_API].transform property must be undefined, or a function'),
'[CALL_API].transform property must be undefined, or a function (validateRSAA)'
);
t.notOk(
isRSAA(action12),
'[CALL_API].transform property must be undefined, or a function (isRSAA)'
);
var action13 = {
[CALL_API]: {

@@ -71,31 +192,64 @@ endpoint: '',

types: ['REQUEST', 'SUCCESS', 'FAILURE'],
body: {},
headers: {},
schema: new Schema('key'),
bailout: ''
}
}), '[CALL_API].schema can also be an array of schemas');
t.notOk(isRSAA({
};
t.ok(
validateRSAA(action13).includes('[CALL_API].bailout property must be undefined, a boolean, or a function'),
'[CALL_API].bailout must be undefined, a boolean or a function (validateRSAA)'
);
t.notOk(
isRSAA(action13),
'[CALL_API].bailout must be undefined, a boolean or a function (isRSAA)'
);
var action14 = {
[CALL_API]: {
endpoint: '',
method: 'GET',
types: ['REQUEST', 'SUCCESS', 'FAILURE'],
body: {},
headers: {},
schema: arrayOf(new Schema('key')),
bailout: ''
types: ['REQUEST', 'SUCCESS', 'FAILURE']
}
}), '[CALL_API].bailout must be a boolean or a function');
t.ok(isRSAA({
};
t.notOk(
validateRSAA(action14).length,
'[CALL_API].endpoint may be a string (validateRSAA)'
);
t.ok(
isRSAA(action14),
'[CALL_API].endpoint may be a string (isRSAA)'
);
var action15 = {
[CALL_API]: {
endpoint: (() => ''),
method: 'GET',
types: ['REQUEST', 'SUCCESS', 'FAILURE']
}
};
t.notOk(
validateRSAA(action15).length,
'[CALL_API].endpoint may be a function (validateRSAA)'
);
t.ok(
isRSAA(action15),
'[CALL_API].endpoint may be a function (isRSAA)'
);
var action16 = {
[CALL_API]: {
endpoint: '',
method: 'GET',
types: ['REQUEST', 'SUCCESS', 'FAILURE'],
body: {},
headers: {},
schema: new Schema('key'),
bailout: false
headers: {}
}
}), 'isRSAA must return true for an RSAA action (1)');
t.ok(isRSAA({
};
t.notOk(
validateRSAA(action16).length,
'[CALL_API].headers may be a plain JavaScript object (validateRSAA)'
);
t.ok(
isRSAA(action16),
'[CALL_API].headers may be a plain JavaScript object (isRSAA)'
);
var action17 = {
[CALL_API]: {

@@ -105,20 +259,48 @@ endpoint: '',

types: ['REQUEST', 'SUCCESS', 'FAILURE'],
body: {},
headers: {},
schema: new Schema('key'),
bailout: (() => false)
transform: () => {}
}
}), 'isRSAA must return true for an RSAA action (2)');
t.ok(isRSAA({
};
t.notOk(
validateRSAA(action17).length,
'[CALL_API].transform may be a function (validateRSAA)'
);
t.ok(
isRSAA(action17),
'[CALL_API].transform may be a function (isRSAA)'
);
var action18 = {
[CALL_API]: {
endpoint: (() => ''),
endpoint: '',
method: 'GET',
types: ['REQUEST', 'SUCCESS', 'FAILURE'],
body: {},
headers: {},
schema: new Schema('key'),
bailout: false
}
}), 'isRSAA must return true for an RSAA action (3)');
};
t.notOk(
validateRSAA(action18).length,
'[CALL_API].bailout may be a boolean (validateRSAA)'
);
t.ok(
isRSAA(action18),
'[CALL_API].bailout may be a boolean (isRSAA)'
);
var action19 = {
[CALL_API]: {
endpoint: '',
method: 'GET',
types: ['REQUEST', 'SUCCESS', 'FAILURE'],
bailout: (() => false)
}
};
t.notOk(
validateRSAA(action19).length,
'[CALL_API].bailout may be a function (validateRSAA)'
);
t.ok(
isRSAA(action19),
'[CALL_API].bailout may be a function (isRSAA)'
);
t.end();

@@ -202,3 +384,3 @@ });

test('apiMiddleware must handle an unsuccessful API request that returns a non-json response', function (t) {
test('apiMiddleware must handle an unsuccessful API request with a non-json response', function (t) {
const api = nock('http://127.0.0.1')

@@ -324,3 +506,3 @@ .get('/api/users/1')

test('apiMiddleware must process a successful API response with a schema when present', function (t) {
test('apiMiddleware must process a successful API response with a transform function when present', function (t) {
const userRecord = {

@@ -360,3 +542,3 @@ id: 1,

types: ['FETCH_USER.REQUEST', 'FETCH_USER.SUCCESS', 'FETCH_USER.FAILURE'],
schema: userSchema
transform: (json) => normalize(json, userSchema)
}

@@ -379,53 +561,2 @@ };

test('apiMiddleware must process a successful API response with an array of schemas when present', function (t) {
const testList = [
{
id: 1,
username: 'Alice',
},
{
id: 2,
username: 'Bob'
}
];
const userSchema = new Schema('users');
const entities = {
users: {
1: {
id: 1,
username: 'Alice'
},
2: {
id: 2,
username: 'Bob'
}
}
};
const api = nock('http://127.0.0.1')
.get('/api/users/1')
.reply(200, testList, {'Content-Type': 'application/json'});
const anAction = {
[CALL_API]: {
endpoint: 'http://127.0.0.1/api/users/1',
method: 'GET',
types: ['FETCH_USER.REQUEST', 'FETCH_USER.SUCCESS', 'FETCH_USER.FAILURE'],
schema: arrayOf(userSchema)
}
};
const doGetState = () => {};
const nextHandler = apiMiddleware({ getState: doGetState });
const doNext = (action) => {
switch (action.type) {
case 'FETCH_USER.SUCCESS':
t.deepEqual(action.payload.entities, entities, 'success FSA has correct payload property');
break;
}
};
const actionHandler = nextHandler(doNext);
t.plan(1);
actionHandler(anAction);
});
test('apiMiddleware must use an endpoint function when present', function (t) {

@@ -457,3 +588,3 @@ const api = nock('http://127.0.0.1')

test('apiMiddleware must use a bailout function when present', function (t) {
test('apiMiddleware must use a bailout boolean when present', function (t) {
const api = nock('http://127.0.0.1')

@@ -460,0 +591,0 @@ .get('/api/users/1')

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc