Skip to content

Commit

Permalink
Merge pull request #102 from spenceralger/config_transactions
Browse files Browse the repository at this point in the history
Config transactions
  • Loading branch information
spenceralger committed May 30, 2014
2 parents 36e1b89 + de8d0d8 commit dc987d1
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 60 deletions.
5 changes: 4 additions & 1 deletion src/kibana/apps/settings/directives/indices/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ define(function (require) {
});

require('modules').get('app/settings')
.controller('kbnSettingsIndicesCreate', function ($scope, $location, Notifier, Private, indexPatterns, es) {
.controller('kbnSettingsIndicesCreate', function ($scope, $location, Notifier, Private, indexPatterns, es, config) {
var notify = new Notifier();
var refreshKibanaIndex = Private(require('./_refresh_kibana_index'));
var MissingIndices = errors.IndexPatternMissingIndices;
Expand Down Expand Up @@ -140,6 +140,9 @@ define(function (require) {
return indexPattern.refreshFields()
.then(refreshKibanaIndex)
.then(function () {
if (!config.get('defaultIndex')) {
config.set('defaultIndex', indexPattern.id);
}
indexPatterns.cache.clear(indexPattern.id);
$location.url('/settings/indices/' + indexPattern.id);
});
Expand Down
11 changes: 9 additions & 2 deletions src/kibana/apps/settings/directives/indices/edit.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
define(function (require) {
var _ = require('lodash');
var module = require('modules').get('app/settings');

require('routes')
.when('/settings/indices/:id', {
template: require('text!../../partials/indices/edit.html'),
Expand All @@ -10,12 +13,13 @@ define(function (require) {
}
});

require('modules').get('app/settings')
.controller('kbnSettingsIndicesEdit', function ($scope, $location, $route, config, courier, Notifier, Private) {
module.controller('kbnSettingsIndicesEdit', function ($scope, $location, $route, config, courier, Notifier, Private) {
var notify = new Notifier();
var refreshKibanaIndex = Private(require('./_refresh_kibana_index'));

$scope.indexPattern = $route.current.locals.indexPattern;
var otherIds = _.without($route.current.locals.indexPatternIds, $scope.indexPattern.id);

$scope.table = {
by: 'name',
reverse: false,
Expand All @@ -30,6 +34,9 @@ define(function (require) {
$scope.removePattern = function () {
if ($scope.indexPattern.id === config.get('defaultIndex')) {
config.delete('defaultIndex');
if (otherIds.length) {
config.set('defaultIndex', otherIds[0]);
}
}

courier.indexPatterns.delete($scope.indexPattern)
Expand Down
80 changes: 80 additions & 0 deletions src/kibana/components/config/_delayed_updater.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
define(function (require) {
return function DelayedUpdaterFactory(Private, $rootScope, Promise, Notifier) {
var notify = new Notifier();
var _ = require('lodash');
var angular = require('angular');
var vals = Private(require('./_vals'));

return function DelayedUpdater(doc) {
var updater = this;
var queue = [];
var log = {};
var timer;

updater.fire = function () {
clearTimeout(timer);

// only fire once
if (updater.fired) return;
updater.fired = true;


var method;
var body;
var updated = [];
var deleted = [];

// seperate the log into lists
Object.keys(log).forEach(function (key) {
if (log[key] === 'updated') updated.push(key);
else deleted.push(key);
});

if (deleted.length) {
method = 'doIndex';
body = _.clone(vals);
} else {
method = 'doUpdate';
body = _.pick(vals, updated);
}

doc[method](vals)
.then(function (resp) {
queue.forEach(function (q) { q.resolve(resp); });
}, function (err) {
queue.forEach(function (q) { q.reject(err); });
});
};

updater.update = function (key, val, silentAndLocal) {
var newVal = val;
var oldVal = vals[key];

if (angular.equals(newVal, oldVal)) {
return Promise.resolve();
}
else if (newVal == null) {
delete vals[key];
log[key] = 'deleted';
}
else {
vals[key] = newVal;
log[key] = 'updated';
}

if (silentAndLocal) return Promise.resolve();

var defer = Promise.defer();
queue.push(defer);
notify.log('config change: ' + key + ': ' + vals[key] + ' -> ' + val);
$rootScope.$broadcast('change:config.' + key, newVal, oldVal);

// reset the fire timer
clearTimeout(timer);
timer = setTimeout(updater.fire, 200);
return defer.promise;
};
};

};
});
5 changes: 5 additions & 0 deletions src/kibana/components/config/_vals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
define(function (require) {
return function ConfigValsService() {
return {};
};
});
100 changes: 43 additions & 57 deletions src/kibana/components/config/config.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,36 @@
define(function (require) {
var angular = require('angular');
var _ = require('lodash');
var nextTick = require('utils/next_tick');
var configFile = require('../../../config');
var defaults = require('./defaults');

require('notify/notify');

var module = require('modules').get('kibana/config', [
'kibana/notify'
]);

// guid within this window
var nextId = (function () {
var i = 0;
return function () {
return ++i;
};
}());

var configFile = require('../../../config');
// allow the rest of the app to get the configFile easily
module.constant('configFile', configFile);

// service for delivering config variables to everywhere else
module.service('config', function (Private, $rootScope, Notifier, kbnVersion, Promise, kbnSetup) {
module.service('config', function (Private, Notifier, kbnVersion, kbnSetup) {
var config = this;

var angular = require('angular');
var _ = require('lodash');
var defaults = require('./defaults');
var DelayedUpdater = Private(require('./_delayed_updater'));
var vals = Private(require('./_vals'));

var notify = new Notifier({
location: 'Config'
});

var DocSource = Private(require('courier/data_source/doc_source'));
// active or previous instance of DelayedUpdater. This will log and then process an
// update once it is requested by calling #set() or #clear().
var updater;

var DocSource = Private(require('courier/data_source/doc_source'));
var doc = (new DocSource())
.index(configFile.kibanaIndex)
.type('config')
.id(kbnVersion);

var vals = null;

/******
* PUBLIC API
******/
Expand All @@ -54,18 +47,24 @@ define(function (require) {
var complete = notify.lifecycle('config init');
return kbnSetup()
.then(function getDoc() {

// used to apply an entire es response to the vals, silentAndLocal will prevent
// event/notifications/writes from occuring.
var applyMassUpdate = function (resp, silentAndLocal) {
_.union(_.keys(resp._source), _.keys(vals)).forEach(function (key) {
change(key, resp._source[key], silentAndLocal);
});
};

return doc.fetch().then(function initDoc(resp) {
if (!resp.found) return doc.doIndex({}).then(getDoc);
else {
vals = _.cloneDeep(resp._source || {});

doc.onUpdate(function applyMassUpdate(resp) {
var allKeys = [].concat(_.keys(vals), _.keys(resp._source));
_.uniq(allKeys).forEach(function (key) {
if (!angular.equals(vals[key], resp._source[key])) {
change(key, resp._source[key]);
}
});
// apply update, and keep it quiet the first time
applyMassUpdate(resp, true);

// don't keep it quiet other times
doc.onUpdate(function (resp) {
applyMassUpdate(resp, false);
});
}
});
Expand All @@ -85,45 +84,32 @@ define(function (require) {
}
};

// sets a value in the config
config.set = function (key, val) {
// sets a value in the config
// the es doc must be updated successfully for the update to reflect in the get api.
if (vals[key] === val) return Promise.resolve(true);

var update = {};
update[key] = val;

return doc.doUpdate(update)
.then(function () {
change(key, val);
return true;
});
return change(key, val);
};

// clears a value from the config
config.clear = function (key) {
if (vals[key] == null) return Promise.resolve(true);

var newVals = _.cloneDeep(vals);
delete newVals[key];

return doc.doIndex(newVals)
.then(function () {
change(key, void 0);
return true;
});
return change(key);
};
// alias for clear
config.delete = config.clear;

config.close = function () {};
config.close = function () {
if (updater) updater.fire();
};

/*****
* PRIVATE API
*****/

var change = function (key, val) {
notify.log('config change: ' + key + ': ' + vals[key] + ' -> ' + val);
vals[key] = val;
$rootScope.$broadcast('change:config.' + key, val, vals[key]);
var change = function (key, val, silentAndLocal) {
// if the previous updater has already fired, then start over with null
if (updater && updater.fired) updater = null;
// create a new updater
if (!updater) updater = new DelayedUpdater(doc);
// return a promise that will be resolved once the action is eventually done
return updater.update(key, val, silentAndLocal);
};

config._vals = function () {
Expand Down

0 comments on commit dc987d1

Please sign in to comment.