(function() { 'use strict'; angular.module('shared') .factory('addCaching', [ '$q', '_', 'ExpiringCache', 'RequestQueue', 'advise', function($q, _, ExpiringCache, RequestQueue, advise) { return function addCaching(api, config) { config = _.defaults(config, { indexer: function(item) { return item ? item.id : null }, serializeRequest: function(item) { var id = _.isObject(item) ? item.id : item; return { id: id } }, isRequestSatisfied: function(cachedItem, request) { return !cachedItem || cachedItem.id == request.id }, getRequestKey: function(request) { return !!request ? request.id : null; }, annotations: {}, locals: {} }); var noCache = {}; var cache = new ExpiringCache(config); var apiQueue = new RequestQueue({ trace: config.trace }); _.forEach(api, function(method, name) { if(!_.isFunction(method)) return method; noCache[name] = function() { return method.apply(api, arguments); }; var middlewares = getMiddlewareForMethod(name); if(middlewares) { var adviceLocals = getLocalsForMethod(name); api[name] = advise(method, middlewares, adviceLocals); } }); return _.extend(api, { noCache: noCache }); function getLocalsForMethod(method) { var annotations = config.annotations[method] || {}; return angular.extend(config.locals, { $cache$: cache, $apiQueue$:apiQueue, $cacheRequest$: { getKey: annotations.getRequestKey || config.getRequestKey, isSatisfied: annotations.isRequestSatisfied || config.isRequestSatisfied }, $cacheResponse$: { index: annotations.indexer || config.indexer } }); } function getMiddlewareForMethod(method) { var annotations = config.annotations[method] || {}; if(!annotations.strategy) return null; var middlewares = annotations.strategy; if( !angular.isArray(middlewares) ) middlewares = [ middlewares ]; var serializeRequest = annotations.serializeRequest || config.serializeRequest; if( !angular.isFunction(serializeRequest) ) throw new Error('serializeRequest must be a function!'); function requestSerializerProvider() { return function(context, next) { var requestSettings = serializeRequest.apply(null, context.arguments) || {}; context.request = angular.extend(context.request || {}, requestSettings); return next(context); } } //TODO should we sequence all requests? middlewares.unshift('apiSequenceRequests', requestSerializerProvider); return middlewares; } } }]) })();