cannery-adapter-rest
Version:
The Cannery REST adapter
352 lines (294 loc) • 12.6 kB
JavaScript
'use strict';
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 ajax = require('then-request');
var debug = require('debug')('cannery-adapter-rest');
var adapterOptions = Symbol();
var endpointState = {};
var endpointResolver = {};
var storage = global.sessionStorage || {};
var RestAdapter = function () {
function RestAdapter() {
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
var routes = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
_classCallCheck(this, RestAdapter);
this.routes = routes;
this[adapterOptions] = options;
}
_createClass(RestAdapter, [{
key: 'buildCacheKey',
value: function buildCacheKey(url) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var key = this.getUrl(url);
if (options.qs) {
key += '-' + JSON.stringify(options.qs);
}
if (options.headers) {
key += '-' + JSON.stringify(options.headers);
}
return key;
}
}, {
key: 'getRoutePath',
value: function getRoutePath(model) {
var routeArray = [model.constructor.name];
var child = model;
while (child.getParent()) {
routeArray.push(child.getParent().constructor.name);
child = child.getParent();
}
return routeArray.reverse().join('/');
}
}, {
key: 'parseOverrideRoute',
value: function parseOverrideRoute(uri, model) {
var parents = [];
var child = model;
while (child.getParent()) {
parents.push(child.getParent().id);
child = child.getParent();
}
uri = uri.replace(/{id}/g, model.id);
for (var i = 0; i < parents.length; i++) {
uri = uri.replace('{parents[' + i + ']}', parents[i]);
}
return uri;
}
}, {
key: 'getOverrideRoute',
value: function getOverrideRoute(requestType, model) {
var path = this.getRoutePath(model);
debug('ROUTE', requestType, path);
if (this.routes[path] && this.routes[path][requestType]) {
return this.parseOverrideRoute(this.routes[path][requestType], model);
}
}
}, {
key: 'buildNestedUrl',
value: function buildNestedUrl(url, child) {
var parent = child.getParent();
url = child.getName() + '/' + child.id + '/' + url;
if (parent) {
return this.buildNestedUrl(url, parent);
} else {
return url;
}
}
}, {
key: 'create',
value: function create(model, options) {
var url = this.getCreateRoute(model);
var requestOptions = this.createOptions(model, options);
requestOptions.body = this.getBody(model);
return ajax('POST', url, requestOptions).then(this.formatFetchResponse.bind(this));
}
}, {
key: 'createOptions',
value: function createOptions(model, options) {
options = options || {};
return Object.assign({}, {
qs: options.qs,
body: options.body,
headers: Object.assign({}, this[adapterOptions].headers, options.headers)
});
}
}, {
key: 'createOptionsWithEtags',
value: function createOptionsWithEtags(model, options, cacheKey) {
var requestOptions = this.createOptions(model, options);
//requestOptions.headers['If-None-Match'] = storage[`cannery-e-${cacheKey}`];
return requestOptions;
}
}, {
key: 'destroy',
value: function destroy(model, options) {
var url = this.getDestroyRoute(model);
var requestOptions = this.createOptions(model, options);
requestOptions.body = model.toJSON();
return ajax('DELETE', url, requestOptions).then(this.parseResponse.bind(this));
}
}, {
key: 'fetch',
value: function fetch(model) {
var _this = this;
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var url = this.getFetchRoute(model);
var cacheKey = this.buildCacheKey(url, options);
var requestOptions = this.createOptionsWithEtags(model, options, cacheKey);
if (endpointState[url] === 'fetching' || endpointState[url] === 'fetched') {
return endpointResolver[url];
}
endpointState[url] = 'fetching';
endpointResolver[url] = ajax('GET', url, requestOptions).then(function (response) {
return _this.formatFetchResponse(response, cacheKey);
}).then(function (response) {
endpointState[url] = 'fetched';
return response;
});
return endpointResolver[url];
}
}, {
key: 'fetchWithin',
value: function fetchWithin(model, parent) {
var _this2 = this;
var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
var url = this.getFetchWithinRoute(model, parent);
var cacheKey = this.buildCacheKey(url, options);
var requestOptions = this.createOptionsWithEtags(model, options, cacheKey);
if (endpointState[url] === 'fetching' || endpointState[url] === 'fetched') {
return endpointResolver[url];
}
endpointState[url] = 'fetching';
endpointResolver[url] = ajax('GET', this.getUrl(url), requestOptions).then(function (response) {
return _this2.formatFetchResponse(response, cacheKey);
}).then(function (response) {
endpointState[url] = 'fetched';
return response;
});
return endpointResolver[url];
}
}, {
key: 'findAll',
value: function findAll(Model) {
var _this3 = this;
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var url = this.getFindAllRoute(Model);
var cacheKey = this.buildCacheKey(url, options);
var requestOptions = this.createOptionsWithEtags(null, options, cacheKey);
return ajax('GET', url, requestOptions).then(function (response) {
return _this3.formatFindAllResponse(response, cacheKey);
});
}
}, {
key: 'findAllWithin',
value: function findAllWithin(Model, parent) {
var _this4 = this;
var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
var url = this.getFindAllWithinRoute(Model, parent);
var cacheKey = this.buildCacheKey(url, options);
var requestOptions = this.createOptionsWithEtags(parent, options, cacheKey);
return ajax('GET', this.getUrl(url), requestOptions).then(function (response) {
return _this4.formatFindAllResponse(response, cacheKey);
});
}
}, {
key: 'formatFetchResponse',
value: function formatFetchResponse(response, cacheKey) {
var body = this.parseResponse(response, cacheKey);
var envelope = this.getOptions().envelope;
return envelope ? body[envelope] : body;
}
}, {
key: 'formatFindAllResponse',
value: function formatFindAllResponse(response, cacheKey) {
var body = this.parseResponse(response, cacheKey);
var envelope = this.getOptions().arrayEnvelope;
return envelope ? body[envelope] : body;
}
}, {
key: 'getBody',
value: function getBody(model) {
return JSON.stringify(model.toJSON());
}
}, {
key: 'getCreateRoute',
value: function getCreateRoute(model) {
return this.getOverrideRoute('create', model) || this.getSingleFetchUrl(model);
}
}, {
key: 'getDestroyRoute',
value: function getDestroyRoute(model) {
return this.getOverrideRoute('destroy', model) || this.getSingleFetchUrl(model);
}
}, {
key: 'getFetchRoute',
value: function getFetchRoute(model) {
return this.getOverrideRoute('fetch', model) || this.getFetchUrl(model.getName(), model.id);
}
}, {
key: 'getFetchWithinRoute',
value: function getFetchWithinRoute(model, parent) {
return this.getOverrideRoute('fetch', model) || this.buildNestedUrl(model.getName(), parent);
}
}, {
key: 'getFindAllRoute',
value: function getFindAllRoute(Model) {
return this.getOverrideRoute('findAll', new Model()) || this.getUrl(new Model().getName());
}
}, {
key: 'getFindAllWithinRoute',
value: function getFindAllWithinRoute(Model, parent) {
var overrideModel = new Model();
overrideModel.getParent = function () {
return parent;
};
return this.getOverrideRoute('findAll', overrideModel) || this.buildNestedUrl(new Model().getName(), parent);
}
}, {
key: 'getUpdateRoute',
value: function getUpdateRoute(model) {
return this.getOverrideRoute('update', model) || this.getSingleFetchUrl(model);
}
}, {
key: 'getUrl',
value: function getUrl(url) {
var _getOptions = this.getOptions();
var urlRoot = _getOptions.urlRoot;
return '' + (urlRoot || '') + url;
}
}, {
key: 'getFetchUrl',
value: function getFetchUrl(url, id) {
if (id) {
return this.getUrl(url) + '/' + id;
}
return this.getUrl(url);
}
}, {
key: 'getSingleFetchUrl',
value: function getSingleFetchUrl(model) {
var parent = model.getParent();
var parentUrl = parent ? parent.getName() + '/' + parent.id + '/' : '';
return this.getFetchUrl(parentUrl + model.getName(), model.id);
}
}, {
key: 'getOptions',
value: function getOptions() {
return this[adapterOptions];
}
}, {
key: 'getResponseBody',
value: function getResponseBody(response, cacheKey) {
var cachedData = storage['cannery-d-' + cacheKey];
if (response.statusCode === 304 && cachedData) {
return cachedData;
}
if (response.headers.etag) {
storage['cannery-e-' + cacheKey] = response.headers.etag;
storage['cannery-d-' + cacheKey] = response.getBody();
}
return response.getBody();
}
}, {
key: 'parseResponse',
value: function parseResponse(response, cacheKey) {
var body = this.getResponseBody(response, cacheKey);
try {
return JSON.parse(body);
} catch (e) {
return {};
}
}
}, {
key: 'update',
value: function update(model) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var url = this.getUpdateRoute(model);
var requestOptions = this.createOptions(model, options);
requestOptions.body = this.getBody(model);
return ajax('PUT', url, requestOptions).then(this.formatFetchResponse.bind(this));
}
}]);
return RestAdapter;
}();
module.exports = RestAdapter;