diff --git a/dist/restangular.js b/dist/restangular.js index dd2e369e..12421b23 100644 --- a/dist/restangular.js +++ b/dist/restangular.js @@ -1,13 +1,26 @@ /** * Restful Resources service for AngularJS apps - * @version v1.5.2 - 2016-02-08 * @link https://github.com/mgonto/restangular + * @version v1.5.2 - 2016-10-26 * @link https://github.com/mgonto/restangular * @author Martin Gontovnikas * @license MIT License, http://www.opensource.org/licenses/MIT - */(function() { + */(function (root, factory) { + // https://github.com/umdjs/umd/blob/master/templates/returnExports.js + if (typeof define === 'function' && define.amd) { + define(['lodash', 'angular'], factory); + } else if (typeof module === 'object' && module.exports) { + module.exports = factory(require('lodash'), require('angular')); + } else { + // No global export, Restangular will register itself as Angular.js module + factory(root._, root.angular); + } +}(this, function (_, angular) { var restangular = angular.module('restangular', []); restangular.provider('Restangular', function() { + // Make $q available on callbacks via late variable fullfillment. + var $Q; + // Configuration var Configurer = {}; Configurer.init = function(object, config) { @@ -61,6 +74,15 @@ restangular.provider('Restangular', function() { 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); }; @@ -188,12 +210,14 @@ restangular.provider('Restangular', function() { 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', @@ -334,10 +358,27 @@ restangular.provider('Restangular', function() { config.fullRequestInterceptor = function(element, operation, path, url, headers, params, httpConfig) { var interceptors = angular.copy(config.requestInterceptors); var defaultRequest = config.defaultInterceptor(element, operation, path, url, headers, params, httpConfig); - return _.reduce(interceptors, function(request, interceptor) { - return _.extend(request, interceptor(request.element, operation, - path, url, request.headers, request.params, request.httpConfig)); - }, defaultRequest); + var promise = $Q.when(defaultRequest); + + _.forEach(interceptors, function (interceptor) { + promise = promise.then(function (request) { + var args = [ + request.element, + operation, + path, + url, + request.headers, + request.params, + request.httpConfig + ]; + + return $Q.when(interceptor.apply(window, args)).then(function (req) { + return _.extend(request, req); + }); + }); + }); + + return promise; }; object.addRequestInterceptor = function(interceptor) { @@ -641,7 +682,7 @@ restangular.provider('Restangular', function() { Path.prototype = new BaseCreator(); Path.prototype.normalizeUrl = function (url){ - var parts = /(http[s]?:\/\/)?(.*)?/.exec(url); + var parts = /((?:http[s]?:)?\/\/)?(.*)?/.exec(url); parts[2] = parts[2].replace(/[\\\/]+/g, '/'); return (typeof parts[1] !== 'undefined')? parts[1] + parts[2] : parts[2]; }; @@ -759,6 +800,8 @@ restangular.provider('Restangular', function() { this.$get = ['$http', '$q', function($http, $q) { + // Fullfill $Q. + $Q = $q; function createServiceForConfiguration(config) { var service = {}; @@ -927,20 +970,16 @@ restangular.provider('Restangular', function() { function addCustomOperation(elem) { elem[config.restangularFields.customOperation] = _.bind(customFunction, elem); - _.each(['put', 'post', 'get', 'delete'], function(oper) { + var requestMethods = { get: customFunction, delete: customFunction }; + _.each(['put', 'patch', 'post'], function(name) { + requestMethods[name] = function(operation, elem, path, params, headers) { + return _.bind(customFunction, this)(operation, path, params, headers, elem); + }; + }); + _.each(requestMethods, function(requestFunc, name) { + var callOperation = name === 'delete' ? 'remove' : name; _.each(['do', 'custom'], function(alias) { - var callOperation = oper === 'delete' ? 'remove' : oper; - var name = alias + oper.toUpperCase(); - var callFunction; - - if (callOperation !== 'put' && callOperation !== 'post') { - callFunction = customFunction; - } else { - callFunction = function(operation, elem, path, params, headers) { - return _.bind(customFunction, this)(operation, path, params, headers, elem); - }; - } - elem[name] = _.bind(callFunction, elem, callOperation); + elem[alias + name.toUpperCase()] = _.bind(requestFunc, elem, callOperation); }); }); elem[config.restangularFields.customGETLIST] = _.bind(fetchFunction, elem); @@ -1006,7 +1045,9 @@ restangular.provider('Restangular', function() { function restangularizeCollectionAndElements(parent, element, route) { var collection = restangularizeCollection(parent, element, route, false); _.each(collection, function(elem) { - restangularizeElem(parent, elem, route, false); + if (elem) { + restangularizeElem(parent, elem, route, false); + } }); return collection; } @@ -1050,9 +1091,6 @@ restangular.provider('Restangular', function() { var url = urlHandler.fetchUrl(this, what); var whatFetched = what || __this[config.restangularFields.route]; - var request = config.fullRequestInterceptor(null, operation, - whatFetched, url, headers || {}, reqParams || {}, this[config.restangularFields.httpConfig] || {}); - var filledArray = []; filledArray = config.transformElem(filledArray, true, whatFetched, service); @@ -1062,7 +1100,10 @@ restangular.provider('Restangular', function() { method = 'jsonp'; } - var okCallback = function(response) { + /** + * Called upon response success. + */ + function okCallback(response) { var resData = response.data; var fullParams = response.config.params; var data = parseResponse(resData, operation, whatFetched, url, response, deferred); @@ -1074,6 +1115,11 @@ restangular.provider('Restangular', function() { if (!_.isArray(data)) { throw new Error('Response for getList SHOULD be an array and not an object or something else'); } + + if (true === config.plainByDefault) { + return resolvePromise(deferred, response, data, filledArray); + } + var processedData = _.map(data, function(elem) { if (!__this[config.restangularFields.restangularCollection]) { return restangularizeElem(__this, elem, what, true, data); @@ -1112,17 +1158,45 @@ restangular.provider('Restangular', function() { filledArray ); } - }; + } - urlHandler.resource(this, $http, request.httpConfig, request.headers, request.params, what, - this[config.restangularFields.etag], operation)[method]().then(okCallback, function error(response) { + /** + * Called upon response or interception error. + */ + function errorCallback(response) { if (response.status === 304 && __this[config.restangularFields.restangularCollection]) { resolvePromise(deferred, response, __this, filledArray); } else if ( _.every(config.errorInterceptors, function(cb) { return cb(response, deferred, okCallback) !== false; }) ) { // triggered if no callback returns false deferred.reject(response); } - }); + } + + /** + * Called with resolved request from interceptors. + */ + function makeRequest(request) { + urlHandler.resource.apply(urlHandler, [ + __this, + $http, + request.httpConfig, + request.headers, + request.params, + what, + __this[config.restangularFields.etag], + operation + ])[method]().then(okCallback, errorCallback); + } + + config.fullRequestInterceptor.apply(config, [ + null, + operation, + whatFetched, + url, + headers || {}, + reqParams || {}, + this[config.restangularFields.httpConfig] || {} + ]).then(makeRequest, errorCallback); return restangularizePromise(deferred.promise, true, filledArray); } @@ -1154,18 +1228,25 @@ restangular.provider('Restangular', function() { if (_.isObject(callObj) && config.isRestangularized(callObj)) { callObj = stripRestangular(callObj); } - var request = config.fullRequestInterceptor(callObj, operation, route, fetchUrl, - headers || {}, resParams || {}, this[config.restangularFields.httpConfig] || {}); var filledObject = {}; filledObject = config.transformElem(filledObject, false, route, service); - var okCallback = function(response) { + /** + * Called upon response success. + */ + function okCallback(response) { var resData = response.data; var fullParams = response.config.params; var elem = parseResponse(resData, operation, route, fetchUrl, response, deferred); + if (elem) { var data; + + if (true === config.plainByDefault) { + return resolvePromise(deferred, response, elem, filledObject); + } + if (operation === 'post' && !__this[config.restangularFields.restangularCollection]) { data = restangularizeElem( __this[config.restangularFields.parentResource], @@ -1193,40 +1274,59 @@ restangular.provider('Restangular', function() { } else { resolvePromise(deferred, response, undefined, filledObject); } - }; + } - var errorCallback = function(response) { + /** + * Called upon response or interception error. + */ + function errorCallback(response) { if (response.status === 304 && config.isSafe(operation)) { resolvePromise(deferred, response, __this, filledObject); } else if ( _.every(config.errorInterceptors, function(cb) { return cb(response, deferred, okCallback) !== false; }) ) { // triggered if no callback returns false deferred.reject(response); } - }; - // Overriding HTTP Method - var callOperation = operation; - var callHeaders = _.extend({}, request.headers); - var isOverrideOperation = config.isOverridenMethod(operation); - if (isOverrideOperation) { - callOperation = 'post'; - callHeaders = _.extend(callHeaders, {'X-HTTP-Method-Override': operation === 'remove' ? 'DELETE' : operation.toUpperCase()}); - } else if (config.jsonp && callOperation === 'get') { - callOperation = 'jsonp'; } - if (config.isSafe(operation)) { + /** + * Called with resolved request from interceptors. + */ + function makeRequest(request) { + // Overring HTTP Method + var callOperation = operation; + var callHeaders = _.extend({}, request.headers); + var isOverrideOperation = config.isOverridenMethod(operation); if (isOverrideOperation) { - urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params, - what, etag, callOperation)[callOperation]({}).then(okCallback, errorCallback); + callOperation = 'post'; + callHeaders = _.extend(callHeaders, {'X-HTTP-Method-Override': operation === 'remove' ? 'DELETE' : operation.toUpperCase()}); + } else if (config.jsonp && callOperation === 'get') { + callOperation = 'jsonp'; + } + + if (config.isSafe(operation)) { + if (isOverrideOperation) { + urlHandler.resource(__this, $http, request.httpConfig, callHeaders, request.params, + what, etag, callOperation)[callOperation]({}).then(okCallback, errorCallback); + } else { + urlHandler.resource(__this, $http, request.httpConfig, callHeaders, request.params, + what, etag, callOperation)[callOperation]().then(okCallback, errorCallback); + } } else { - urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params, - what, etag, callOperation)[callOperation]().then(okCallback, errorCallback); + urlHandler.resource(__this, $http, request.httpConfig, callHeaders, request.params, + what, etag, callOperation)[callOperation](request.element).then(okCallback, errorCallback); } - } else { - urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params, - what, etag, callOperation)[callOperation](request.element).then(okCallback, errorCallback); } + config.fullRequestInterceptor.apply(window, [ + callObj, + operation, + route, + fetchUrl, + headers || {}, + resParams || {}, + this[config.restangularFields.httpConfig] || {} + ]).then(makeRequest, errorCallback); + return restangularizePromise(deferred.promise, false, filledObject); } @@ -1351,5 +1451,5 @@ restangular.provider('Restangular', function() { return createServiceForConfiguration(globalConfiguration); }]; }); - -})(); + return restangular.name; +})); diff --git a/dist/restangular.min.js b/dist/restangular.min.js index f0599d58..fdec4dc2 100644 --- a/dist/restangular.min.js +++ b/dist/restangular.min.js @@ -1,6 +1,6 @@ /** * Restful Resources service for AngularJS apps - * @version v1.5.2 - 2016-02-08 * @link https://github.com/mgonto/restangular + * @version v1.5.2 - 2016-10-26 * @link https://github.com/mgonto/restangular * @author Martin Gontovnikas * @license MIT License, http://www.opensource.org/licenses/MIT - */!function(){var a=angular.module("restangular",[]);a.provider("Restangular",function(){var a={};a.init=function(a,b){function c(a,b,c,d){var e={};return _.each(_.keys(d),function(f){var g=d[f];g.params=_.extend({},g.params,a.defaultRequestParams[g.method.toLowerCase()]),_.isEmpty(g.params)&&delete g.params,a.isSafe(g.method)?e[f]=function(){return b(_.extend(g,{url:c}))}:e[f]=function(a){return b(_.extend(g,{url:c,data:a}))}}),e}a.configuration=b;var d=["get","head","options","trace","getlist"];b.isSafe=function(a){return _.includes(d,a.toLowerCase())};var e=/^https?:\/\//i;b.isAbsoluteUrl=function(a){return _.isUndefined(b.absoluteUrl)||_.isNull(b.absoluteUrl)?a&&e.test(a):b.absoluteUrl},b.absoluteUrl=_.isUndefined(b.absoluteUrl)?!0:b.absoluteUrl,a.setSelfLinkAbsoluteUrl=function(a){b.absoluteUrl=a},b.baseUrl=_.isUndefined(b.baseUrl)?"":b.baseUrl,a.setBaseUrl=function(a){return b.baseUrl=/\/$/.test(a)?a.substring(0,a.length-1):a,this},b.extraFields=b.extraFields||[],a.setExtraFields=function(a){return b.extraFields=a,this},b.defaultHttpFields=b.defaultHttpFields||{},a.setDefaultHttpFields=function(a){return b.defaultHttpFields=a,this},b.withHttpValues=function(a,c){return _.defaults(c,a,b.defaultHttpFields)},b.encodeIds=_.isUndefined(b.encodeIds)?!0:b.encodeIds,a.setEncodeIds=function(a){b.encodeIds=a},b.defaultRequestParams=b.defaultRequestParams||{get:{},post:{},put:{},remove:{},common:{}},a.setDefaultRequestParams=function(a,c){var d=[],e=c||a;return _.isUndefined(c)?d.push("common"):_.isArray(a)?d=a:d.push(a),_.each(d,function(a){b.defaultRequestParams[a]=e}),this},a.requestParams=b.defaultRequestParams,b.defaultHeaders=b.defaultHeaders||{},a.setDefaultHeaders=function(c){return b.defaultHeaders=c,a.defaultHeaders=b.defaultHeaders,this},a.defaultHeaders=b.defaultHeaders,b.methodOverriders=b.methodOverriders||[],a.setMethodOverriders=function(a){var c=_.extend([],a);return b.isOverridenMethod("delete",c)&&c.push("remove"),b.methodOverriders=c,this},b.jsonp=_.isUndefined(b.jsonp)?!1:b.jsonp,a.setJsonp=function(a){b.jsonp=a},b.isOverridenMethod=function(a,c){var d=c||b.methodOverriders;return!_.isUndefined(_.find(d,function(b){return b.toLowerCase()===a.toLowerCase()}))},b.urlCreator=b.urlCreator||"path",a.setUrlCreator=function(a){if(!_.has(b.urlCreatorFactory,a))throw new Error("URL Path selected isn't valid");return b.urlCreator=a,this},b.restangularFields=b.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",customPOST:"customPOST",customDELETE:"customDELETE",customGET:"customGET",customGETLIST:"customGETLIST",customOperation:"customOperation",doPUT:"doPUT",doPOST:"doPOST",doDELETE:"doDELETE",doGET:"doGET",doGETLIST:"doGETLIST",fromServer:"fromServer",withConfig:"withConfig",withHttpConfig:"withHttpConfig",singleOne:"singleOne",plain:"plain",save:"save",restangularized:"restangularized"},a.setRestangularFields=function(a){return b.restangularFields=_.extend(b.restangularFields,a),this},b.isRestangularized=function(a){return!!a[b.restangularFields.restangularized]},b.setFieldToElem=function(a,b,c){var d=a.split("."),e=b;return _.each(_.initial(d),function(a){e[a]={},e=e[a]}),e[_.last(d)]=c,this},b.getFieldFromElem=function(a,b){var c=a.split("."),d=b;return _.each(c,function(a){d&&(d=d[a])}),angular.copy(d)},b.setIdToElem=function(a,c){return b.setFieldToElem(b.restangularFields.id,a,c),this},b.getIdFromElem=function(a){return b.getFieldFromElem(b.restangularFields.id,a)},b.isValidId=function(a){return""!==a&&!_.isUndefined(a)&&!_.isNull(a)},b.setUrlToElem=function(a,c){return b.setFieldToElem(b.restangularFields.selfLink,a,c),this},b.getUrlFromElem=function(a){return b.getFieldFromElem(b.restangularFields.selfLink,a)},b.useCannonicalId=_.isUndefined(b.useCannonicalId)?!1:b.useCannonicalId,a.setUseCannonicalId=function(a){return b.useCannonicalId=a,this},b.getCannonicalIdFromElem=function(a){var c=a[b.restangularFields.cannonicalId],d=b.isValidId(c)?c:b.getIdFromElem(a);return d},b.responseInterceptors=b.responseInterceptors||[],b.defaultResponseInterceptor=function(a){return a},b.responseExtractor=function(a,c,d,e,f,g){var h=angular.copy(b.responseInterceptors);h.push(b.defaultResponseInterceptor);var i=a;return _.each(h,function(a){i=a(i,c,d,e,f,g)}),i},a.addResponseInterceptor=function(a){return b.responseInterceptors.push(a),this},b.errorInterceptors=b.errorInterceptors||[],a.addErrorInterceptor=function(a){return b.errorInterceptors.push(a),this},a.setResponseInterceptor=a.addResponseInterceptor,a.setResponseExtractor=a.addResponseInterceptor,a.setErrorInterceptor=a.addErrorInterceptor,b.requestInterceptors=b.requestInterceptors||[],b.defaultInterceptor=function(a,b,c,d,e,f,g){return{element:a,headers:e,params:f,httpConfig:g}},b.fullRequestInterceptor=function(a,c,d,e,f,g,h){var i=angular.copy(b.requestInterceptors),j=b.defaultInterceptor(a,c,d,e,f,g,h);return _.reduce(i,function(a,b){return _.extend(a,b(a.element,c,d,e,a.headers,a.params,a.httpConfig))},j)},a.addRequestInterceptor=function(a){return b.requestInterceptors.push(function(b,c,d,e,f,g,h){return{headers:f,params:g,element:a(b,c,d,e),httpConfig:h}}),this},a.setRequestInterceptor=a.addRequestInterceptor,a.addFullRequestInterceptor=function(a){return b.requestInterceptors.push(a),this},a.setFullRequestInterceptor=a.addFullRequestInterceptor,b.onBeforeElemRestangularized=b.onBeforeElemRestangularized||function(a){return a},a.setOnBeforeElemRestangularized=function(a){return b.onBeforeElemRestangularized=a,this},a.setRestangularizePromiseInterceptor=function(a){return b.restangularizePromiseInterceptor=a,this},b.onElemRestangularized=b.onElemRestangularized||function(a){return a},a.setOnElemRestangularized=function(a){return b.onElemRestangularized=a,this},b.shouldSaveParent=b.shouldSaveParent||function(){return!0},a.setParentless=function(a){return _.isArray(a)?b.shouldSaveParent=function(b){return!_.includes(a,b)}:_.isBoolean(a)&&(b.shouldSaveParent=function(){return!a}),this},b.suffix=_.isUndefined(b.suffix)?null:b.suffix,a.setRequestSuffix=function(a){return b.suffix=a,this},b.transformers=b.transformers||{},a.addElementTransformer=function(c,d,e){var f=null,g=null;2===arguments.length?g=d:(g=e,f=d);var h=b.transformers[c];return h||(h=b.transformers[c]=[]),h.push(function(a,b){return _.isNull(f)||a===f?g(b):b}),a},a.extendCollection=function(b,c){return a.addElementTransformer(b,!0,c)},a.extendModel=function(b,c){return a.addElementTransformer(b,!1,c)},b.transformElem=function(a,c,d,e,f){if(!f&&!b.transformLocalElements&&!a[b.restangularFields.fromServer])return a;var g=b.transformers[d],h=a;return g&&_.each(g,function(a){h=a(c,h)}),b.onElemRestangularized(h,c,d,e)},b.transformLocalElements=_.isUndefined(b.transformLocalElements)?!1:b.transformLocalElements,a.setTransformOnlyServerElements=function(a){b.transformLocalElements=!a},b.fullResponse=_.isUndefined(b.fullResponse)?!1:b.fullResponse,a.setFullResponse=function(a){return b.fullResponse=a,this},b.urlCreatorFactory={};var f=function(){};f.prototype.setConfig=function(a){return this.config=a,this},f.prototype.parentsArray=function(a){for(var b=[];a;)b.push(a),a=a[this.config.restangularFields.parentResource];return b.reverse()},f.prototype.resource=function(a,d,e,f,g,h,i,j){var k=_.defaults(g||{},this.config.defaultRequestParams.common),l=_.defaults(f||{},this.config.defaultHeaders);i&&(b.isSafe(j)?l["If-None-Match"]=i:l["If-Match"]=i);var m=this.base(a);if(h){var n="";/\/$/.test(m)||(n+="/"),n+=h,m+=n}return this.config.suffix&&-1===m.indexOf(this.config.suffix,m.length-this.config.suffix.length)&&!this.config.getUrlFromElem(a)&&(m+=this.config.suffix),a[this.config.restangularFields.httpConfig]=void 0,c(this.config,d,m,{getList:this.config.withHttpValues(e,{method:"GET",params:k,headers:l}),get:this.config.withHttpValues(e,{method:"GET",params:k,headers:l}),jsonp:this.config.withHttpValues(e,{method:"jsonp",params:k,headers:l}),put:this.config.withHttpValues(e,{method:"PUT",params:k,headers:l}),post:this.config.withHttpValues(e,{method:"POST",params:k,headers:l}),remove:this.config.withHttpValues(e,{method:"DELETE",params:k,headers:l}),head:this.config.withHttpValues(e,{method:"HEAD",params:k,headers:l}),trace:this.config.withHttpValues(e,{method:"TRACE",params:k,headers:l}),options:this.config.withHttpValues(e,{method:"OPTIONS",params:k,headers:l}),patch:this.config.withHttpValues(e,{method:"PATCH",params:k,headers:l})})};var g=function(){};g.prototype=new f,g.prototype.normalizeUrl=function(a){var b=/(http[s]?:\/\/)?(.*)?/.exec(a);return b[2]=b[2].replace(/[\\\/]+/g,"/"),"undefined"!=typeof b[1]?b[1]+b[2]:b[2]},g.prototype.base=function(a){var c=this;return _.reduce(this.parentsArray(a),function(a,d){var e,f=c.config.getUrlFromElem(d);if(f){if(c.config.isAbsoluteUrl(f))return f;e=f}else if(e=d[c.config.restangularFields.route],d[c.config.restangularFields.restangularCollection]){var g=d[c.config.restangularFields.ids];g&&(e+="/"+g.join(","))}else{var h;h=c.config.useCannonicalId?c.config.getCannonicalIdFromElem(d):c.config.getIdFromElem(d),b.isValidId(h)&&!d.singleOne&&(e+="/"+(c.config.encodeIds?encodeURIComponent(h):h))}return a=a.replace(/\/$/,"")+"/"+e,c.normalizeUrl(a)},this.config.baseUrl)},g.prototype.fetchUrl=function(a,b){var c=this.base(a);return b&&(c+="/"+b),c},g.prototype.fetchRequestedUrl=function(a,c){function d(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c);return b.sort()}function e(a,b,c){for(var e=d(a),f=0;f