UNPKG

fractal-devour-client

Version:

A lightweight, framework agnostic, flexible JSON API client

562 lines (509 loc) 19.3 kB
'use strict'; 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; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var pluralize = require('pluralize'); // Import only what we use from lodash. var _isUndefined = require('lodash/isUndefined'); var _isString = require('lodash/isString'); var _isPlainObject = require('lodash/isPlainObject'); var _isArray = require('lodash/isArray'); var _defaultsDeep = require('lodash/defaultsDeep'); var _forOwn = require('lodash/forOwn'); var _clone = require('lodash/clone'); var _get = require('lodash/get'); var _set = require('lodash/set'); var _hasIn = require('lodash/hasIn'); var _last = require('lodash/last'); var _map = require('lodash/map'); var _findIndex = require('lodash/findIndex'); require('es6-promise').polyfill(); var deserialize = require('./middleware/json-api/_deserialize'); var serialize = require('./middleware/json-api/_serialize'); var Logger = require('./logger'); /* * == JsonApiMiddleware * * Here we construct the middleware stack that will handle building and making * requests, as well as serializing and deserializing our payloads. Users can * easily construct their own middleware layers that adhere to different * standards. * */ var jsonApiHttpBasicAuthMiddleware = require('./middleware/json-api/req-http-basic-auth'); var jsonApiPostMiddleware = require('./middleware/json-api/req-post'); var jsonApiPatchMiddleware = require('./middleware/json-api/req-patch'); var jsonApiPutMiddleware = require('./middleware/json-api/req-put'); var jsonApiDeleteMiddleware = require('./middleware/json-api/req-delete'); var jsonApiGetMiddleware = require('./middleware/json-api/req-get'); var jsonApiHeadersMiddleware = require('./middleware/json-api/req-headers'); var railsParamsSerializer = require('./middleware/json-api/rails-params-serializer'); var sendRequestMiddleware = require('./middleware/request'); var deserializeResponseMiddleware = require('./middleware/json-api/res-deserialize'); var processErrors = require('./middleware/json-api/res-errors'); var jsonApiMiddleware = [jsonApiHttpBasicAuthMiddleware, jsonApiPostMiddleware, jsonApiPatchMiddleware, jsonApiPutMiddleware, jsonApiDeleteMiddleware, jsonApiGetMiddleware, jsonApiHeadersMiddleware, railsParamsSerializer, sendRequestMiddleware, processErrors, deserializeResponseMiddleware]; var JsonApi = function () { function JsonApi() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, JsonApi); if (!(arguments.length === 2 && _isString(arguments[0]) && _isArray(arguments[1])) && !(arguments.length === 1 && (_isPlainObject(arguments[0]) || _isString(arguments[0])))) { throw new Error('Invalid argument, initialize Devour with an object.'); } var defaults = { middleware: jsonApiMiddleware, logger: true, resetBuilderOnCall: true, auth: {}, trailingSlash: { collection: false, resource: false } }; var deprecatedConstructors = function deprecatedConstructors(args) { return args.length === 2 || args.length === 1 && _isString(args[0]); }; if (deprecatedConstructors(arguments)) { defaults.apiUrl = arguments[0]; if (arguments.length === 2) { defaults.middleware = arguments[1]; } } options = _defaultsDeep(options, defaults); var middleware = options.middleware; this._originalMiddleware = middleware.slice(0); this.middleware = middleware.slice(0); this.headers = {}; this.axios = options.axiosInstance; this.auth = options.auth; this.models = {}; this.deserialize = deserialize; this.serialize = serialize; this.builderStack = []; this.resetBuilderOnCall = !!options.resetBuilderOnCall; if (options.pluralize === false) { this.pluralize = function (s) { return s; }; this.pluralize.singular = function (s) { return s; }; } else if ('pluralize' in options) { this.pluralize = options.pluralize; } else { this.pluralize = pluralize; } this.trailingSlash = options.trailingSlash === true ? _forOwn(_clone(defaults.trailingSlash), function (v, k, o) { _set(o, k, true); }) : options.trailingSlash; options.logger ? Logger.enable() : Logger.disable(); if (deprecatedConstructors(arguments)) { Logger.warn('Constructor (apiUrl, middleware) has been deprecated, initialize Devour with an object.'); } } _createClass(JsonApi, [{ key: 'enableLogging', value: function enableLogging() { var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; enabled ? Logger.enable() : Logger.disable(); } }, { key: 'one', value: function one(model, id) { this.builderStack.push({ model: model, id: id, path: this.resourcePathFor(model, id) }); return this; } }, { key: 'all', value: function all(model) { this.builderStack.push({ model: model, path: this.collectionPathFor(model) }); return this; } }, { key: 'relationships', value: function relationships() { this.builderStack.push({ path: 'relationships' }); return this; } }, { key: 'resetBuilder', value: function resetBuilder() { this.builderStack = []; } }, { key: 'stackForResource', value: function stackForResource() { return _hasIn(_last(this.builderStack), 'id'); } }, { key: 'addSlash', value: function addSlash() { return this.stackForResource() ? this.trailingSlash.resource : this.trailingSlash.collection; } }, { key: 'buildPath', value: function buildPath() { return _map(this.builderStack, 'path').join('/'); } }, { key: 'buildUrl', value: function buildUrl() { var path = this.buildPath(); var slash = path !== '' && this.addSlash() ? '/' : ''; return '/' + path + slash; } }, { key: 'get', value: function get() { var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var req = _extends({ method: 'GET', url: this.urlFor(), data: {} }, config); if (this.resetBuilderOnCall) { this.resetBuilder(); } return this.runMiddleware(req); } }, { key: 'post', value: function post(payload) { var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var meta = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var lastRequest = _last(this.builderStack); var req = _extends({ method: 'POST', url: this.urlFor(), model: _get(lastRequest, 'model'), data: payload }, config, { meta: meta }); if (this.resetBuilderOnCall) { this.resetBuilder(); } return this.runMiddleware(req); } }, { key: 'patch', value: function patch(payload) { var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var meta = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var lastRequest = _last(this.builderStack); var req = _extends({ method: 'PATCH', url: this.urlFor(), model: _get(lastRequest, 'model'), data: payload }, config, { meta: meta }); if (this.resetBuilderOnCall) { this.resetBuilder(); } return this.runMiddleware(req); } }, { key: 'customRequest', value: function customRequest() { var method = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'GET'; var url = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.urlFor(); var model = arguments[2]; var payload = arguments[3]; var config = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; var meta = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; var req = _extends({ method: method, url: url, model: model, data: payload }, config, { meta: meta }); return this.runMiddleware(req); } }, { key: 'destroy', value: function destroy() { var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var req = null; if (arguments.length >= 2) { // destroy (modelName, id, [payload], [meta]) var _ref = [].concat(Array.prototype.slice.call(arguments)), model = _ref[0], id = _ref[1], data = _ref[2], reqConfig = _ref[3], meta = _ref[4]; console.assert(model, 'No model specified'); console.assert(id, 'No ID specified'); req = _extends({ method: 'DELETE', url: this.urlFor({ model: model, id: id }), model: model }, reqConfig, { data: data || {}, meta: meta || {} }); } else { // destroy ([payload]) // TODO: find a way to pass meta var lastRequest = _last(this.builderStack); req = _extends({ method: 'DELETE', url: this.urlFor() }, config, { model: _get(lastRequest, 'model'), data: arguments.length === 1 ? arguments[0] : {} }); if (this.resetBuilderOnCall) { this.resetBuilder(); } } return this.runMiddleware(req); } }, { key: 'insertMiddlewareBefore', value: function insertMiddlewareBefore(middlewareName, newMiddleware) { this.insertMiddleware(middlewareName, 'before', newMiddleware); } }, { key: 'insertMiddlewareAfter', value: function insertMiddlewareAfter(middlewareName, newMiddleware) { this.insertMiddleware(middlewareName, 'after', newMiddleware); } }, { key: 'insertMiddleware', value: function insertMiddleware(middlewareName, direction, newMiddleware) { var middleware = this.middleware.filter(function (middleware) { return middleware.name === middlewareName; }); if (middleware.length > 0) { var index = this.middleware.indexOf(middleware[0]); if (direction === 'after') { index = index + 1; } this.middleware.splice(index, 0, newMiddleware); } } }, { key: 'replaceMiddleware', value: function replaceMiddleware(middlewareName, newMiddleware) { var index = _findIndex(this.middleware, ['name', middlewareName]); this.middleware[index] = newMiddleware; } }, { key: 'define', value: function define(modelName, attributes) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; this.models[modelName] = { attributes: attributes, options: options }; } }, { key: 'resetMiddleware', value: function resetMiddleware() { this.middleware = this._originalMiddleware.slice(0); } }, { key: 'applyRequestMiddleware', value: function applyRequestMiddleware(promise) { var requestMiddlewares = this.middleware.filter(function (middleware) { return middleware.req; }); requestMiddlewares.forEach(function (middleware) { promise = promise.then(middleware.req); }); return promise; } }, { key: 'applyResponseMiddleware', value: function applyResponseMiddleware(promise) { var responseMiddleware = this.middleware.filter(function (middleware) { return middleware.res; }); responseMiddleware.forEach(function (middleware) { promise = promise.then(middleware.res); }); return promise; } }, { key: 'applyErrorMiddleware', value: function applyErrorMiddleware(promise) { var errorsMiddleware = this.middleware.filter(function (middleware) { return middleware.error; }); errorsMiddleware.forEach(function (middleware) { promise = promise.then(middleware.error); }); return promise; } }, { key: 'runMiddleware', value: function runMiddleware(req) { var _this = this; var payload = { req: req, jsonApi: this }; var requestPromise = Promise.resolve(payload); requestPromise = this.applyRequestMiddleware(requestPromise); return requestPromise.then(function (res) { payload.res = res; var responsePromise = Promise.resolve(payload); return _this.applyResponseMiddleware(responsePromise); }).catch(function (err) { Logger.error(err); var errorPromise = Promise.resolve(err); return _this.applyErrorMiddleware(errorPromise).then(function (err) { return Promise.reject(err); }); }); } }, { key: 'request', value: function request(url) { var method = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'GET'; var config = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var data = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; var req = _extends({ url: url, method: method }, config, { data: data }); return this.runMiddleware(req); } }, { key: 'find', value: function find(modelName, id) { var config = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var req = _extends({ method: 'GET', url: this.urlFor({ model: modelName, id: id }), model: modelName, data: {} }, config); return this.runMiddleware(req); } }, { key: 'findAll', value: function findAll(modelName) { var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var req = _extends({ method: 'GET', url: this.urlFor({ model: modelName }), model: modelName }, config, { data: {} }); return this.runMiddleware(req); } }, { key: 'create', value: function create(modelName, payload) { var config = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var meta = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; var req = _extends({ method: 'POST', url: this.urlFor({ model: modelName }), model: modelName }, config, { data: payload, meta: meta }); return this.runMiddleware(req); } }, { key: 'update', value: function update(modelName, payload) { var config = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var meta = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; var req = _extends({ method: 'PATCH', url: this.urlFor({ model: modelName, id: payload.id }), model: modelName, data: payload }, config, { meta: meta }); return this.runMiddleware(req); } }, { key: 'put', value: function put(modelName, payload) { var config = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var meta = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; var req = _extends({ method: 'PUT', url: this.urlFor({ model: modelName, id: payload.id }), model: modelName, data: payload }, config, { meta: meta }); return this.runMiddleware(req); } }, { key: 'modelFor', value: function modelFor(modelName) { if (!this.models[modelName]) { throw new Error('API resource definition for model "' + modelName + '" not found. Available models: ' + Object.keys(this.models)); } return this.models[modelName]; } }, { key: 'collectionPathFor', value: function collectionPathFor(modelName) { var collectionPath = _get(this.models[modelName], 'options.collectionPath') || this.pluralize(modelName); return '' + collectionPath; } }, { key: 'resourcePathFor', value: function resourcePathFor(modelName, id) { var collectionPath = this.collectionPathFor(modelName); var customPath = _get(this.models[modelName], 'options.templatePath'); var path = customPath ? customPath.replace(':id', id) : collectionPath + '/' + encodeURIComponent(id); return path; } }, { key: 'collectionUrlFor', value: function collectionUrlFor(modelName) { var collectionPath = this.collectionPathFor(modelName); var trailingSlash = this.trailingSlash['collection'] ? '/' : ''; return '/' + collectionPath + trailingSlash; } }, { key: 'resourceUrlFor', value: function resourceUrlFor(modelName, id) { var resourcePath = this.resourcePathFor(modelName, id); var trailingSlash = this.trailingSlash['resource'] ? '/' : ''; return '/' + resourcePath + trailingSlash; } }, { key: 'urlFor', value: function urlFor() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; if (!_isUndefined(options.model) && !_isUndefined(options.id)) { return this.resourceUrlFor(options.model, options.id); } else if (!_isUndefined(options.model)) { return this.collectionUrlFor(options.model); } else { return this.buildUrl(); } } }, { key: 'pathFor', value: function pathFor() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; if (!_isUndefined(options.model) && !_isUndefined(options.id)) { return this.resourcePathFor(options.model, options.id); } else if (!_isUndefined(options.model)) { return this.collectionPathFor(options.model); } else { return this.buildPath(); } } }, { key: 'addInterceptor', value: function addInterceptor(type) { var _axios$interceptors$t; for (var _len = arguments.length, interceptors = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { interceptors[_key - 1] = arguments[_key]; } (_axios$interceptors$t = this.axios.interceptors[type]).use.apply(_axios$interceptors$t, interceptors); } }]); return JsonApi; }(); module.exports = JsonApi;