Fixes tree cache - fixes the unit tests associated with the tree cache so they run properly, tree cache can now be cleared by section, by key, by both or all. There's no longer a default cache key since we don't want to cache things when not needed. ONLY the main trees are now cached, tree pickers and dialog trees should never be cached since tree structures change all of the time (i.e. by other users, changing node names, etc...). The tree-picker directive also had a cache which meant that anyone using that directive would end up with the same cache.

This commit is contained in:
Shannon
2013-11-01 11:35:48 +11:00
parent 35f49962ca
commit 19a1ca64f6
13 changed files with 218 additions and 79 deletions

View File

@@ -70,7 +70,7 @@ angular.module("umbraco.directives")
if (scope.eventhandler) {
scope.eventhandler.clearCache = function(section){
treeService.clearCache(section);
treeService.clearCache({ section: section });
};
scope.eventhandler.load = function(section){
@@ -110,7 +110,7 @@ angular.module("umbraco.directives")
enableDeleteAnimations = false;
//use $q.when because a promise OR raw data might be returned.
$q.when(treeService.getTree({ section: scope.section, tree: scope.treealias, cachekey: scope.cachekey, isDialog: scope.isdialog ? scope.isdialog : false }))
treeService.getTree({ section: scope.section, tree: scope.treealias, cacheKey: scope.cachekey, isDialog: scope.isdialog ? scope.isdialog : false })
.then(function (data) {
//set the data once we have it
scope.tree = data;

View File

@@ -288,7 +288,7 @@ angular.module('umbraco.services')
reloadSection: function (sectionAlias) {
if(this.ui.treeEventHandler){
this.ui.treeEventHandler.clearCache(sectionAlias);
this.ui.treeEventHandler.clearCache({ section: sectionAlias });
this.ui.treeEventHandler.load(sectionAlias);
}
},

View File

@@ -15,14 +15,12 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
var standardCssClass = 'icon umb-tree-icon sprTree';
function getCacheKey(args) {
if (!args) {
args = {
section: 'content',
cacheKey: ''
};
}
//if there is no cache key they return null - it won't be cached.
if (!args || !args.cacheKey) {
return null;
}
var cacheKey = args.cachekey;
var cacheKey = args.cacheKey;
cacheKey += "_" + args.section;
return cacheKey;
}
@@ -103,15 +101,35 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
return undefined;
},
/** clears the tree cache - with optional sectionAlias */
clearCache: function(sectionAlias) {
if (!sectionAlias) {
/** clears the tree cache - with optional cacheKey and optional section */
clearCache: function (args) {
//clear all if not specified
if (!args) {
treeCache = {};
}
else {
var cacheKey = getCacheKey({ section: sectionAlias });
if (treeCache && treeCache[cacheKey] != null) {
treeCache = _.omit(treeCache, cacheKey);
//if section and cache key specified just clear that cache
if (args.section && args.cacheKey) {
var cacheKey = getCacheKey(args);
if (cacheKey && treeCache && treeCache[cacheKey] != null) {
treeCache = _.omit(treeCache, cacheKey);
}
}
else if (args.cacheKey) {
//if only the cache key is specified, then clear all cache starting with that key
var allKeys1 = _.keys(treeCache);
var toRemove1 = _.filter(allKeys1, function (k) {
return k.startsWith(args.cacheKey + "_");
});
treeCache = _.omit(treeCache, toRemove1);
}
else if (args.section) {
//if only the section is specified then clear all cache regardless of cache key by that section
var allKeys2 = _.keys(treeCache);
var toRemove2 = _.filter(allKeys2, function (k) {
return k.endsWith("_" + args.section);
});
treeCache = _.omit(treeCache, toRemove2);
}
}
},
@@ -240,25 +258,28 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
return "";
},
/** gets the tree, returns a promise */
getTree: function (args) {
var deferred = $q.defer();
//set defaults
if (!args) {
args = {
section: 'content',
cacheKey : ''
};
args = { section: 'content', cacheKey: null };
}
else if (!args.section) {
args.section = 'content';
}
var cacheKey = getCacheKey(args);
//return the cache if it exists
if (treeCache[cacheKey] !== undefined){
return treeCache[cacheKey];
if (cacheKey && treeCache[cacheKey] !== undefined) {
deferred.resolve(treeCache[cacheKey]);
}
var self = this;
return treeResource.loadApplication(args)
treeResource.loadApplication(args)
.then(function(data) {
//this will be called once the tree app data has loaded
var result = {
@@ -268,13 +289,19 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
};
//we need to format/modify some of the node data to be used in our app.
self._formatNodeDataForUseInUI(result.root, result.root.children, args.section);
//cache this result
//TODO: We'll need to un-cache this in many circumstances
treeCache[cacheKey] = result;
//return the data result as promised
//deferred.resolve(treeArray[cacheKey]);
return treeCache[cacheKey];
//cache this result if a cache key is specified - generally a cache key should ONLY
// be specified for application trees, dialog trees should not be cached.
if (cacheKey) {
treeCache[cacheKey] = result;
deferred.resolve(treeCache[cacheKey]);
}
//return un-cached
deferred.resolve(result);
});
return deferred.promise;
},
getMenu: function (args) {

View File

@@ -38,7 +38,6 @@
<div ng-hide="showSearch">
<umb-tree
section="content"
cachekey="contentpickerDialog"
showheader="true"
showoptions="false"
isdialog="true"

View File

@@ -22,7 +22,6 @@
<umb-tree
section="content"
cachekey="linkpickerDialog"
showheader="true"
showoptions="false"
eventhandler="dialogTreeEventHandler"

View File

@@ -7,7 +7,6 @@
<umb-tree
section="member"
treealias="memberGroup"
cachekey="membergrouppickerDialog"
showheader="false"
showoptions="false"
isdialog="true"

View File

@@ -39,7 +39,6 @@
<umb-tree
section="member"
treealias="member"
cachekey="memberpickerDialog"
showheader="false"
showoptions="false"
isdialog="true"

View File

@@ -36,7 +36,6 @@
<umb-tree
section="{{section}}"
treealias="{{treeAlias}}"
cachekey="treepickerDialog"
showheader="true"
showoptions="false"
isdialog="true"

View File

@@ -21,9 +21,9 @@
<div ng-hide="success">
<umb-tree
section="content"
cachekey="copypickerDialog"
showheader="true"
showoptions="false"
isdialog="true"
eventhandler="dialogTreeEventHandler">
</umb-tree>

View File

@@ -21,9 +21,9 @@
<div ng-hide="success">
<umb-tree
section="content"
cachekey="movepickerDialog"
showheader="true"
showoptions="false"
isdialog="true"
eventhandler="dialogTreeEventHandler">
</umb-tree>
</div>

View File

@@ -63,6 +63,7 @@
<!-- the tree -->
<div id="tree" class="umb-modalcolumn-body">
<umb-tree
cachekey="_"
eventhandler="treeEventHandler"
path="{{nav.ui.currentPath}}"
section="{{nav.ui.currentSection}}"

View File

@@ -21,7 +21,6 @@
<div ng-hide="success">
<umb-tree
section="media"
cachekey="movepickerDialog"
showheader="true"
showoptions="false"
eventhandler="dialogTreeEventHandler">

View File

@@ -1,5 +1,5 @@
describe('tree service tests', function () {
var treeService;
var treeService, $rootScope, $httpBackend, mocks;
function getContentTree() {
@@ -37,66 +37,183 @@ describe('tree service tests', function () {
}
beforeEach(module('umbraco.services'));
beforeEach(module('umbraco.resources'));
beforeEach(module('umbraco.mocks'));
beforeEach(inject(function ($injector) {
beforeEach(inject(function ($injector, mocksUtils) {
//for these tests we don't want any authorization to occur
mocksUtils.disableAuth();
$rootScope = $injector.get('$rootScope');
$httpBackend = $injector.get('$httpBackend');
mocks = $injector.get("treeMocks");
mocks.register();
treeService = $injector.get('treeService');
}));
describe('tree cache', function () {
it('tree with no args caches as content', function () {
treeService.getTree().then(function(data) {
//it('tree with section but no cache key does not cache', function () {
// treeService.getTree().then(function (data) {
var cache = treeService._getTreeCache();
expect(cache).toBeDefined();
expect(cache["_content"]).toBeDefined();
// var cache = treeService._getTreeCache();
// expect(cache).toBeDefined();
// expect(cache["_content"]).toBeDefined();
});
});
it('tree caches by section', function () {
treeService.getTree({section: "media"}).then(function (data) {
// });
//});
var cache = treeService._getTreeCache();
expect(cache).toBeDefined();
expect(cache["_media"]).toBeDefined();
it('does not cache with no args', function () {
});
});
it('removes cache by section', function () {
treeService.getTree({ section: "media" }).then(function (data) {
var cache = treeService._getTreeCache();
expect(cache["_media"]).toBeDefined();
expect(_.keys(cache).length).toBe(1);
treeService.clearCache("media");
var cache;
treeService.getTree().then(function (data) {
cache = treeService._getTreeCache();
expect(cache["_media"]).not.toBeDefined();
expect(_.keys(cache).length).toBe(0);
});
$rootScope.$digest();
$httpBackend.flush();
expect(_.keys(cache).length).toBe(0);
});
it('does not cache with no cacheKey', function () {
var cache;
treeService.getTree({section: "content"}).then(function (data) {
cache = treeService._getTreeCache();
});
$rootScope.$digest();
$httpBackend.flush();
expect(_.keys(cache).length).toBe(0);
});
it('caches by section with cache key', function () {
var cache;
treeService.getTree({ section: "media", cacheKey: "_" }).then(function (data) {
cache = treeService._getTreeCache();
});
$rootScope.$digest();
$httpBackend.flush();
expect(cache["__media"]).toBeDefined();
});
it('caches by default content section with cache key', function () {
var cache;
treeService.getTree({ cacheKey: "_" }).then(function (data) {
cache = treeService._getTreeCache();
});
$rootScope.$digest();
$httpBackend.flush();
expect(cache).toBeDefined();
expect(cache["__content"]).toBeDefined();
});
it('removes by section with cache key', function () {
var cache;
treeService.getTree({ section: "media", cacheKey: "_" }).then(function (data) {
treeService.getTree({ section: "content", cacheKey: "_" }).then(function (d) {
cache = treeService._getTreeCache();
});
});
$rootScope.$digest();
$httpBackend.flush();
expect(cache["__media"]).toBeDefined();
expect(_.keys(cache).length).toBe(2);
treeService.clearCache({ section: "media", cacheKey: "_" });
cache = treeService._getTreeCache();
expect(cache["__media"]).not.toBeDefined();
expect(_.keys(cache).length).toBe(1);
});
it('removes cache by key regardless of section', function () {
var cache;
treeService.getTree({ section: "media", cacheKey: "_" }).then(function (data) {
treeService.getTree({ section: "content", cacheKey: "_" }).then(function (d) {
treeService.getTree({ section: "content", cacheKey: "anotherkey" }).then(function (dd) {
cache = treeService._getTreeCache();
});
});
});
$rootScope.$digest();
$httpBackend.flush();
expect(cache["__media"]).toBeDefined();
expect(cache["__content"]).toBeDefined();
expect(_.keys(cache).length).toBe(3);
treeService.clearCache({ cacheKey: "_" });
cache = treeService._getTreeCache();
expect(cache["__media"]).not.toBeDefined();
expect(cache["__content"]).not.toBeDefined();
expect(_.keys(cache).length).toBe(1);
});
it('removes all section cache regardless of key', function () {
var cache;
treeService.getTree({ section: "media", cacheKey: "_" }).then(function (data) {
treeService.getTree({ section: "media", cacheKey: "anotherkey" }).then(function (d) {
treeService.getTree({ section: "content", cacheKey: "anotherkey" }).then(function (dd) {
cache = treeService._getTreeCache();
});
});
});
$rootScope.$digest();
$httpBackend.flush();
expect(cache["anotherkey_media"]).toBeDefined();
expect(cache["__media"]).toBeDefined();
expect(_.keys(cache).length).toBe(3);
treeService.clearCache({ section: "media" });
cache = treeService._getTreeCache();
expect(cache["anotherkey_media"]).not.toBeDefined();
expect(cache["__media"]).not.toBeDefined();
expect(_.keys(cache).length).toBe(1);
});
it('removes all cache', function () {
treeService.getTree({ section: "media" }).then(function (data) {
treeService.getTree({ section: "content" }).then(function (d) {
var cache;
var cache = treeService._getTreeCache();
expect(cache["_content"]).toBeDefined();
expect(cache["_media"]).toBeDefined();
expect(_.keys(cache).length).toBe(2);
treeService.clearCache();
cache = treeService._getTreeCache();
expect(cache["_content"]).toBeDefined();
expect(cache["_media"]).toBeDefined();
expect(_.keys(cache).length).toBe(0);
treeService.getTree({ section: "media", cacheKey: "_" }).then(function (data) {
treeService.getTree({ section: "media", cacheKey: "anotherkey" }).then(function (d) {
treeService.getTree({ section: "content", cacheKey: "anotherkey" }).then(function(dd) {
cache = treeService._getTreeCache();
});
});
});
$rootScope.$digest();
$httpBackend.flush();
expect(cache["anotherkey_media"]).toBeDefined();
expect(cache["__media"]).toBeDefined();
expect(cache["anotherkey_content"]).toBeDefined();
expect(_.keys(cache).length).toBe(3);
treeService.clearCache();
cache = treeService._getTreeCache();
expect(_.keys(cache).length).toBe(0);
});
});