Wrap $http for caching

We'll need an option to disable caching for specific routes, or ideally,
specify other means of caching requests.

This commit adds:
  - A wrapper for $http to catch caching options passed through
  Restangular
  - An improved CacheService that can handle loading promises
  (previously, it interfaced only to Resources)
pull/4239/head
Oliver Günther 9 years ago
parent 8b2a068ede
commit 7be07f9a1c
  1. 78
      frontend/app/components/api/api-v3/api-v3.cache-config.ts
  2. 7
      frontend/app/components/api/api-work-packages/api-work-packages.service.ts
  3. 72
      frontend/app/components/caching/cache-service.service.ts
  4. 7
      frontend/app/services/index.js

@ -0,0 +1,78 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
function apiV3CacheConfig($provide) {
// Add caching wrapper around $http using angular-cache
$provide.decorator('$http', function($delegate, CacheService) {
var $http = $delegate;
var wrapper = function() {
var params = arguments;
var request = arguments[0];
var requestable = _ => $http.apply($http, params);
var useCaching = request.cache || true;
var cacheOptions;
// Override cache values from headers
if (request.headers && request.headers.caching) {
cacheOptions = request.headers.caching;
useCaching = cacheOptions.enabled;
delete request.headers.caching;
}
// Do not cache anything but GET coming from Restangualr
if (!useCaching || (request.method && request.method !== 'GET')) {
request.cache = false;
return requestable();
}
if (useCaching) {
return CacheService.cachedPromise(requestable, request.url);
}
};
Object.keys($http).forEach(function(key) {
// Decorate all fns with our cached wrapper
if (typeof $http[key] === 'function') {
wrapper[key] = function() {
return $http[key].apply($http, arguments);
};
} else {
wrapper[key] = $http[key];
}
});
return wrapper;
});
}
angular
.module('openproject.api')
.config(apiV3CacheConfig);

@ -44,7 +44,12 @@ export class ApiWorkPackagesService {
workPackages = uncachedProvider.service('work_packages');
}
return workPackages.getList(this.queryAsV3Params(offset, pageSize, query));
return workPackages.getList(
this.queryAsV3Params(offset, pageSize, query),
{
caching: { enabled : false }
}
);
}
private uncachedAPI():restangular.IService {

@ -26,10 +26,7 @@
// See doc/COPYRIGHT.rdoc for more details.
//++
module.exports = function(HALAPIResource,
$http,
$q,
CacheFactory) {
function CacheService($q, CacheFactory) {
// Temporary storage for currently resolving promises
var _promises = {};
@ -52,6 +49,12 @@ module.exports = function(HALAPIResource,
});
},
memoryStorage: function() {
return CacheService.customCache('openproject-memory_storage_cache', {
storageMode: 'memory'
});
},
customCache: function(identifier, params) {
var _cache = CacheFactory.get(identifier);
@ -78,33 +81,21 @@ module.exports = function(HALAPIResource,
disabled = true;
},
loadResource: function(resource, force) {
var key = resource.props.href,
cache = CacheService.temporaryCache(),
cachedValue,
_fetchResource = function() {
var deferred = $q.defer();
resource.fetch().then(function(data) {
cache.put(key, data);
deferred.resolve(data);
}, function() {
deferred.reject();
cache.remove(key);
});
return deferred.promise;
};
cachedPromise: function(promiseFn, key, options) {
options = options || {};
var cache = options.cache || CacheService.memoryStorage();
var force = options.force || false;
var deferred = $q.defer();
var cachedValue, promise;
// Return early when frontend caching is not desired
if (cache.disabled) {
return _fetchResource();
return promiseFn();
}
// Got the result directly? Great.
cachedValue = cache.get(key);
if (cachedValue && !force) {
var deferred = $q.defer();
deferred.resolve(cachedValue);
return deferred.promise;
}
@ -116,11 +107,40 @@ module.exports = function(HALAPIResource,
return _promises[key];
}
var promise = _fetchResource();
_promises[key] = promise;
return promise;
// Call now to retrieve promise
promise = promiseFn();
promise
.then(data => {
cache.put(key, data);
deferred.resolve(data);
})
.catch(error => {
deferred.reject(error);
cache.remove(key);
})
.finally(_ => delete _promises[key]);
_promises[key] = deferred.promise;
return deferred.promise;
},
loadResource: function(resource, force) {
return CacheService.cachedPromise(
(_ => resource.fetch()),
resource.props.href,
{
cache: CacheService.temporaryCache(),
force: force
}
);
}
};
return CacheService;
};
angular
.module('openproject.services')
.factory('CacheService', CacheService);

@ -34,13 +34,6 @@ angular.module('openproject.services')
'NotificationsService',
require('./activity-service')
])
.service('CacheService', [
'HALAPIResource',
'$http',
'$q',
'CacheFactory',
require('./cache-service')
])
.service('GroupService', ['$http', 'PathHelper', require('./group-service')])
.service('HookService', require('./hook-service'))
.service('KeyboardShortcutService', [

Loading…
Cancel
Save