UNPKG

ng2-restangular

Version:
676 lines 25.7 kB
"use strict"; var _ = require('lodash'); function RestangularConfigurer(object, config) { object.configuration = config; /** * Those are HTTP safe methods for which there is no need to pass any data with the request. */ var safeMethods = ['get', 'head', 'options', 'trace', 'getlist']; config.isSafe = function (operation) { return _.includes(safeMethods, operation.toLowerCase()); }; var absolutePattern = /^https?:\/\//i; config.isAbsoluteUrl = function (string) { return _.isUndefined(config.absoluteUrl) || _.isNull(config.absoluteUrl) ? string && absolutePattern.test(string) : config.absoluteUrl; }; config.absoluteUrl = _.isUndefined(config.absoluteUrl) ? true : config.absoluteUrl; object.setSelfLinkAbsoluteUrl = function (value) { config.absoluteUrl = value; }; /** * This is the BaseURL to be used with Restangular */ config.baseUrl = _.isUndefined(config.baseUrl) ? '' : config.baseUrl; object.setBaseUrl = function (newBaseUrl) { config.baseUrl = /\/$/.test(newBaseUrl) ? newBaseUrl.substring(0, newBaseUrl.length - 1) : newBaseUrl; return this; }; /** * Sets the extra fields to keep from the parents */ config.extraFields = config.extraFields || []; object.setExtraFields = function (newExtraFields) { config.extraFields = newExtraFields; return this; }; /** * Some default $http parameter to be used in EVERY call **/ config.defaultHttpFields = config.defaultHttpFields || {}; object.setDefaultHttpFields = function (values) { config.defaultHttpFields = values; return this; }; /** * Always return plain data, no restangularized object **/ config.plainByDefault = config.plainByDefault || false; object.setPlainByDefault = function (value) { config.plainByDefault = value === true ? true : false; return this; }; config.withHttpValues = function (httpLocalConfig, obj) { return _.defaults(obj, httpLocalConfig, config.defaultHttpFields); }; config.encodeIds = _.isUndefined(config.encodeIds) ? true : config.encodeIds; object.setEncodeIds = function (encode) { config.encodeIds = encode; }; config.defaultRequestParams = config.defaultRequestParams || { get: {}, post: {}, put: {}, remove: {}, common: {} }; object.setDefaultRequestParams = function (param1, param2) { var methods = [], params = param2 || param1; if (!_.isUndefined(param2)) { if (_.isArray(param1)) { methods = param1; } else { methods.push(param1); } } else { methods.push('common'); } _.each(methods, function (method) { config.defaultRequestParams[method] = params; }); return this; }; object.requestParams = config.defaultRequestParams; config.defaultHeaders = config.defaultHeaders || {}; object.setDefaultHeaders = function (headers) { config.defaultHeaders = headers; object.defaultHeaders = config.defaultHeaders; return this; }; object.defaultHeaders = config.defaultHeaders; /** * Method overriders response Method **/ config.defaultResponseMethod = config.defaultResponseMethod || 'promise'; object.setDefaultResponseMethod = function (method) { config.defaultResponseMethod = method; object.defaultResponseMethod = config.defaultResponseMethod; return this; }; object.defaultResponseMethod = config.defaultResponseMethod; /** * Method overriders will set which methods are sent via POST with an X-HTTP-Method-Override **/ config.methodOverriders = config.methodOverriders || []; object.setMethodOverriders = function (values) { var overriders = _.extend([], values); if (config.isOverridenMethod('delete', overriders)) { overriders.push('remove'); } config.methodOverriders = overriders; return this; }; config.jsonp = _.isUndefined(config.jsonp) ? false : config.jsonp; object.setJsonp = function (active) { config.jsonp = active; }; config.isOverridenMethod = function (method, values) { var search = values || config.methodOverriders; return !_.isUndefined(_.find(search, function (one) { return one.toLowerCase() === method.toLowerCase(); })); }; /** * Sets the URL creator type. For now, only Path is created. In the future we'll have queryParams **/ config.urlCreator = config.urlCreator || 'path'; object.setUrlCreator = function (name) { if (!_.has(config.urlCreatorFactory, name)) { throw new Error('URL Path selected isn\'t valid'); } config.urlCreator = name; return this; }; /** * You can set the restangular fields here. The 3 required fields for Restangular are: * * id: Id of the element * route: name of the route of this element * parentResource: the reference to the parent resource * * All of this fields except for id, are handled (and created) by Restangular. By default, * the field values will be id, route and parentResource respectively */ config.restangularFields = config.restangularFields || { id: 'id', route: 'route', parentResource: 'parentResource', restangularCollection: 'restangularCollection', cannonicalId: '__cannonicalId', etag: 'restangularEtag', selfLink: 'href', get: 'get', getList: 'getList', put: 'put', post: 'post', remove: 'remove', head: 'head', trace: 'trace', options: 'options', patch: 'patch', getRestangularUrl: 'getRestangularUrl', getRequestedUrl: 'getRequestedUrl', putElement: 'putElement', addRestangularMethod: 'addRestangularMethod', getParentList: 'getParentList', clone: 'clone', ids: 'ids', httpConfig: '_$httpConfig', reqParams: 'reqParams', one: 'one', all: 'all', several: 'several', oneUrl: 'oneUrl', allUrl: 'allUrl', customPUT: 'customPUT', customPATCH: 'customPATCH', customPOST: 'customPOST', customDELETE: 'customDELETE', customGET: 'customGET', customGETLIST: 'customGETLIST', customOperation: 'customOperation', doPUT: 'doPUT', doPATCH: 'doPATCH', doPOST: 'doPOST', doDELETE: 'doDELETE', doGET: 'doGET', doGETLIST: 'doGETLIST', fromServer: 'fromServer', withConfig: 'withConfig', withHttpConfig: 'withHttpConfig', singleOne: 'singleOne', plain: 'plain', save: 'save', restangularized: 'restangularized' }; object.setRestangularFields = function (resFields) { config.restangularFields = _.extend(config.restangularFields, resFields); return this; }; config.isRestangularized = function (obj) { return !!obj[config.restangularFields.restangularized]; }; config.setFieldToElem = function (field, elem, value) { var properties = field.split('.'); var idValue = elem; _.each(_.initial(properties), function (prop) { idValue[prop] = {}; idValue = idValue[prop]; }); var index = _.last(properties); idValue[index] = value; return this; }; config.getFieldFromElem = function (field, elem) { var properties = field.split('.'); var idValue = elem; _.each(properties, function (prop) { if (idValue) { idValue = idValue[prop]; } }); return _.clone(idValue); }; config.setIdToElem = function (elem, id /*, route */) { config.setFieldToElem(config.restangularFields.id, elem, id); return this; }; config.getIdFromElem = function (elem) { return config.getFieldFromElem(config.restangularFields.id, elem); }; config.isValidId = function (elemId) { return '' !== elemId && !_.isUndefined(elemId) && !_.isNull(elemId); }; config.setUrlToElem = function (elem, url /*, route */) { config.setFieldToElem(config.restangularFields.selfLink, elem, url); return this; }; config.getUrlFromElem = function (elem) { return config.getFieldFromElem(config.restangularFields.selfLink, elem); }; config.useCannonicalId = _.isUndefined(config.useCannonicalId) ? false : config.useCannonicalId; object.setUseCannonicalId = function (value) { config.useCannonicalId = value; return this; }; config.getCannonicalIdFromElem = function (elem) { var cannonicalId = elem[config.restangularFields.cannonicalId]; var actualId = config.isValidId(cannonicalId) ? cannonicalId : config.getIdFromElem(elem); return actualId; }; /** * Sets the Response parser. This is used in case your response isn't directly the data. * For example if you have a response like {meta: {'meta'}, data: {name: 'Gonto'}} * you can extract this data which is the one that needs wrapping * * The ResponseExtractor is a function that receives the response and the method executed. */ config.responseInterceptors = config.responseInterceptors || []; config.defaultResponseInterceptor = function (data /*, operation, what, url, response, subject */) { return data || {}; }; config.responseExtractor = function (data, operation, what, url, response, subject) { var interceptors = _.clone(config.responseInterceptors); interceptors.push(config.defaultResponseInterceptor); var theData = data; _.each(interceptors, function (interceptor) { theData = interceptor(theData, operation, what, url, response, subject); }); return theData; }; object.addResponseInterceptor = function (extractor) { config.responseInterceptors.push(extractor); return this; }; config.errorInterceptors = config.errorInterceptors || []; object.addErrorInterceptor = function (interceptor) { config.errorInterceptors.push(interceptor); return this; }; object.setResponseInterceptor = object.addResponseInterceptor; object.setResponseExtractor = object.addResponseInterceptor; object.setErrorInterceptor = object.addErrorInterceptor; /** * Response interceptor is called just before resolving promises. */ /** * Request interceptor is called before sending an object to the server. */ config.requestInterceptors = config.requestInterceptors || []; config.defaultInterceptor = function (element, operation, path, url, headers, params, httpConfig) { return { element: element, headers: headers, params: params, httpConfig: httpConfig }; }; config.fullRequestInterceptor = function (element, operation, path, url, headers, params, httpConfig) { var interceptors = _.clone(config.requestInterceptors); var defaultRequest = config.defaultInterceptor(element, operation, path, url, headers, params, httpConfig); return _.reduce(interceptors, function (request, interceptor) { var returnInterceptor = interceptor(request.element, operation, path, url, request.headers, request.params, request.httpConfig); return _.extend(request, returnInterceptor); }, defaultRequest); }; object.addRequestInterceptor = function (interceptor) { config.requestInterceptors.push(function (elem, operation, path, url, headers, params, httpConfig) { return { headers: headers, params: params, element: interceptor(elem, operation, path, url), httpConfig: httpConfig }; }); return this; }; object.setRequestInterceptor = object.addRequestInterceptor; object.addFullRequestInterceptor = function (interceptor) { config.requestInterceptors.push(interceptor); return this; }; object.setFullRequestInterceptor = object.addFullRequestInterceptor; config.onBeforeElemRestangularized = config.onBeforeElemRestangularized || function (elem) { return elem; }; object.setOnBeforeElemRestangularized = function (post) { config.onBeforeElemRestangularized = post; return this; }; object.setRestangularizePromiseInterceptor = function (interceptor) { config.restangularizePromiseInterceptor = interceptor; return this; }; /** * This method is called after an element has been "Restangularized". * * It receives the element, a boolean indicating if it's an element or a collection * and the name of the model * */ config.onElemRestangularized = config.onElemRestangularized || function (elem) { return elem; }; object.setOnElemRestangularized = function (post) { config.onElemRestangularized = post; return this; }; config.shouldSaveParent = config.shouldSaveParent || function () { return true; }; object.setParentless = function (values) { if (_.isArray(values)) { config.shouldSaveParent = function (route) { return !_.includes(values, route); }; } else if (_.isBoolean(values)) { config.shouldSaveParent = function () { return !values; }; } return this; }; /** * This lets you set a suffix to every request. * * For example, if your api requires that for JSon requests you do /users/123.json, you can set that * in here. * * * By default, the suffix is null */ config.suffix = _.isUndefined(config.suffix) ? null : config.suffix; object.setRequestSuffix = function (newSuffix) { config.suffix = newSuffix; return this; }; /** * Add element transformers for certain routes. */ config.transformers = config.transformers || {}; object.addElementTransformer = function (type, secondArg, thirdArg) { var isCollection = null; var transformer = null; if (arguments.length === 2) { transformer = secondArg; } else { transformer = thirdArg; isCollection = secondArg; } var typeTransformers = config.transformers[type]; if (!typeTransformers) { typeTransformers = config.transformers[type] = []; } typeTransformers.push(function (coll, elem) { if (_.isNull(isCollection) || (coll === isCollection)) { return transformer(elem); } return elem; }); return object; }; object.extendCollection = function (route, fn) { return object.addElementTransformer(route, true, fn); }; object.extendModel = function (route, fn) { return object.addElementTransformer(route, false, fn); }; config.transformElem = function (elem, isCollection, route, Restangular, force) { if (!force && !config.transformLocalElements && !elem[config.restangularFields.fromServer]) { return elem; } var typeTransformers = config.transformers[route]; var changedElem = elem; if (typeTransformers) { _.each(typeTransformers, function (transformer) { changedElem = transformer(isCollection, changedElem); }); } return config.onElemRestangularized(changedElem, isCollection, route, Restangular); }; config.transformLocalElements = _.isUndefined(config.transformLocalElements) ? false : config.transformLocalElements; object.setTransformOnlyServerElements = function (active) { config.transformLocalElements = !active; }; config.fullResponse = _.isUndefined(config.fullResponse) ? false : config.fullResponse; object.setFullResponse = function (full) { config.fullResponse = full; return this; }; //Internal values and functions config.urlCreatorFactory = {}; /** * Base URL Creator. Base prototype for everything related to it **/ var BaseCreator = function () { }; BaseCreator.prototype.setConfig = function (config) { this.config = config; return this; }; BaseCreator.prototype.parentsArray = function (current) { var parents = []; while (current) { parents.push(current); current = current[this.config.restangularFields.parentResource]; } return parents.reverse(); }; function RestangularResource(config, $http, url, configurer) { var resource = {}; _.each(_.keys(configurer), function (key) { var value = configurer[key]; // Add default parameters value.params = _.extend({}, value.params, config.defaultRequestParams[value.method.toLowerCase()]); // We don't want the ? if no params are there if (_.isEmpty(value.params)) { delete value.params; } if (config.isSafe(value.method)) { resource[key] = function () { var config = _.extend(value, { url: url }); return $http.createRequest(config); }; } else { resource[key] = function (data) { var config = _.extend(value, { url: url, data: data }); return $http.createRequest(config); }; } }); return resource; } BaseCreator.prototype.resource = function (current, $http, localHttpConfig, callHeaders, callParams, what, etag, operation) { var params = _.defaults(callParams || {}, this.config.defaultRequestParams.common); var headers = _.defaults(callHeaders || {}, this.config.defaultHeaders); if (etag) { if (!config.isSafe(operation)) { headers['If-Match'] = etag; } else { headers['If-None-Match'] = etag; } } var url = this.base(current); if (what) { var add = ''; if (!/\/$/.test(url)) { add += '/'; } add += what; url += add; } if (this.config.suffix && url.indexOf(this.config.suffix, url.length - this.config.suffix.length) === -1 && !this.config.getUrlFromElem(current)) { url += this.config.suffix; } current[this.config.restangularFields.httpConfig] = undefined; return RestangularResource(this.config, $http, url, { getList: this.config.withHttpValues(localHttpConfig, { method: 'GET', params: params, headers: headers }), get: this.config.withHttpValues(localHttpConfig, { method: 'GET', params: params, headers: headers }), jsonp: this.config.withHttpValues(localHttpConfig, { method: 'jsonp', params: params, headers: headers }), put: this.config.withHttpValues(localHttpConfig, { method: 'PUT', params: params, headers: headers }), post: this.config.withHttpValues(localHttpConfig, { method: 'POST', params: params, headers: headers }), remove: this.config.withHttpValues(localHttpConfig, { method: 'DELETE', params: params, headers: headers }), head: this.config.withHttpValues(localHttpConfig, { method: 'HEAD', params: params, headers: headers }), trace: this.config.withHttpValues(localHttpConfig, { method: 'TRACE', params: params, headers: headers }), options: this.config.withHttpValues(localHttpConfig, { method: 'OPTIONS', params: params, headers: headers }), patch: this.config.withHttpValues(localHttpConfig, { method: 'PATCH', params: params, headers: headers }) }); }; /** * This is the Path URL creator. It uses Path to show Hierarchy in the Rest API. * This means that if you have an Account that then has a set of Buildings, a URL to a building * would be /accounts/123/buildings/456 **/ var Path = function () { }; Path.prototype = new BaseCreator(); Path.prototype.normalizeUrl = function (url) { var parts = /((?:http[s]?:)?\/\/)?(.*)?/.exec(url); parts[2] = parts[2].replace(/[\\\/]+/g, '/'); return (typeof parts[1] !== 'undefined') ? parts[1] + parts[2] : parts[2]; }; Path.prototype.base = function (current) { var __this = this; return _.reduce(this.parentsArray(current), function (acum, elem) { var elemUrl; var elemSelfLink = __this.config.getUrlFromElem(elem); if (elemSelfLink) { if (__this.config.isAbsoluteUrl(elemSelfLink)) { return elemSelfLink; } else { elemUrl = elemSelfLink; } } else { elemUrl = elem[__this.config.restangularFields.route]; if (elem[__this.config.restangularFields.restangularCollection]) { var ids = elem[__this.config.restangularFields.ids]; if (ids) { elemUrl += '/' + ids.join(','); } } else { var elemId; if (__this.config.useCannonicalId) { elemId = __this.config.getCannonicalIdFromElem(elem); } else { elemId = __this.config.getIdFromElem(elem); } if (config.isValidId(elemId) && !elem.singleOne) { elemUrl += '/' + (__this.config.encodeIds ? encodeURIComponent(elemId) : elemId); } } } acum = acum.replace(/\/$/, '') + '/' + elemUrl; return __this.normalizeUrl(acum); }, this.config.baseUrl); }; Path.prototype.fetchUrl = function (current, what) { var baseUrl = this.base(current); if (what) { baseUrl += '/' + what; } return baseUrl; }; Path.prototype.fetchRequestedUrl = function (current, what) { var url = this.fetchUrl(current, what); var params = current[config.restangularFields.reqParams]; // From here on and until the end of fetchRequestedUrl, // the code has been kindly borrowed from angular.js // The reason for such code bloating is coherence: // If the user were to use this for cache management, the // serialization of parameters would need to be identical // to the one done by angular for cache keys to match. function sortedKeys(obj) { var keys = []; for (var key in obj) { if (obj.hasOwnProperty(key)) { keys.push(key); } } return keys.sort(); } function forEachSorted(obj, iterator, context) { var keys = sortedKeys(obj); for (var i = 0; i < keys.length; i++) { iterator.call(context, obj[keys[i]], keys[i]); } return keys; } function encodeUriQuery(val, pctEncodeSpaces) { return encodeURIComponent(val).replace(/%40/gi, '@').replace(/%3A/gi, ':').replace(/%24/g, '$').replace(/%2C/gi, ',').replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); } if (!params) { return url + (this.config.suffix || ''); } var parts = []; forEachSorted(params, function (value, key) { if (value === null || value === undefined) { return; } if (!_.isArray(value)) { value = [value]; } _.forEach(value, function (v) { if (_.isObject(v)) { v = JSON.stringify(v); } parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(v)); }); }); return url + (this.config.suffix || '') + ((url.indexOf('?') === -1) ? '?' : '&') + parts.join('&'); }; config.urlCreatorFactory.path = Path; } exports.RestangularConfigurer = RestangularConfigurer; //# sourceMappingURL=ng2-restangular-config.factory.js.map