Merge pull request #1617 from w33ble/indexPattern/loading-2

Index pattern/loading - after team review
This commit is contained in:
Joe Fleming 2014-10-09 12:30:36 -07:00
commit 900a26dc8b
12 changed files with 201 additions and 208 deletions

View file

@ -174,31 +174,23 @@ define(function (require) {
};
$scope.unlink = function () {
return searchSource.getParent(true)
.then(function (parent) {
var parent = searchSource.getParent(true);
var parentsParent = parent.getParent(true);
return parent.getParent(true)
.then(function (parentsParent) {
// parentsParent can be undefined
// display unlinking for 2 seconds, unless it is double clicked
$scope.unlinking = $timeout($scope.doneUnlinking, 2000);
delete savedVis.savedSearchId;
var q = searchSource.get('query');
$state.query = q;
var searchState = parent.toJSON();
// display unlinking for 2 seconds, unless it is double clicked
$scope.unlinking = $timeout($scope.doneUnlinking, 2000);
delete savedVis.savedSearchId;
var q = searchSource.get('query');
$state.query = q;
// copy over all state except "aggs"
_(searchState).omit('aggs').forOwn(function (val, key) {
searchSource.set(key, val);
});
var searchState = parent.toJSON();
// copy over all state except "aggs"
_(searchState).omit('aggs').forOwn(function (val, key) {
searchSource.set(key, val);
});
searchSource.inherits(parentsParent);
courier.setRootSearchSource(searchSource);
});
}).catch(notify.fatal);
searchSource.inherits(parentsParent);
};
$scope.doneUnlinking = function () {

View file

@ -24,8 +24,7 @@ define(function (require) {
title: 'string',
visState: 'json',
description: 'string',
savedSearchId: 'string',
indexPattern: 'string'
savedSearchId: 'string'
},
defaults: {
@ -37,11 +36,11 @@ define(function (require) {
return def;
}()),
description: '',
savedSearchId: opts.savedSearchId,
indexPattern: opts.indexPattern
savedSearchId: opts.savedSearchId
},
searchSource: true,
indexPattern: opts.indexPattern,
afterESResp: this._afterEsResp
});
@ -49,45 +48,20 @@ define(function (require) {
SavedVis.prototype._afterEsResp = function () {
var self = this;
var relatedSearch = self.savedSearchId;
var relatedPattern = !relatedSearch && self.indexPattern;
var linkedSearch = self.savedSearchId;
var promisedParent = (function () {
if (relatedSearch) {
// returns a promise
return savedSearches.get(self.savedSearchId);
}
var fakeSavedSearch = {
searchSource: courier.createSource('search')
};
if (relatedPattern) {
return courier.indexPatterns.get(relatedPattern)
.then(function (indexPattern) {
fakeSavedSearch.searchSource.index(indexPattern);
return fakeSavedSearch;
});
}
return Promise.resolve(fakeSavedSearch);
}());
return promisedParent
return Promise.resolve(linkedSearch && savedSearches.get(linkedSearch))
.then(function (parent) {
self.savedSearch = parent;
self.searchSource
.inherits(parent.searchSource)
.size(0);
if (!self.vis) {
self.vis = self._createVis();
} else {
self.vis.indexPattern = self.searchSource.get('index');
self.vis.setState(self.visState);
if (parent) {
self.savedSearch = parent;
self.searchSource.inherits(parent.searchSource);
}
self.searchSource.size(0);
return self.vis ? self._updateVis() : self._createVis();
})
.then(function (vis) {
self.searchSource.aggs(function () {
return self.vis.aggs.toDsl();
});
@ -97,13 +71,23 @@ define(function (require) {
};
SavedVis.prototype._createVis = function () {
var indexPattern = this.searchSource.get('index');
var self = this;
if (this.stateJSON) {
this.visState = Vis.convertOldState(this.typeName, JSON.parse(this.stateJSON));
if (self.stateJSON) {
self.visState = Vis.convertOldState(self.typeName, JSON.parse(self.stateJSON));
}
return new Vis(indexPattern, this.visState);
return self.vis = new Vis(
self.searchSource.get('index'),
self.visState
);
};
SavedVis.prototype._updateVis = function () {
var self = this;
self.vis.indexPattern = self.searchSource.get('index');
self.vis.setState(self.visState);
};
return SavedVis;

View file

@ -67,7 +67,7 @@ define(function (require) {
var defer = Promise.defer();
queue.push(defer);
notify.log('config change: ' + key + ': ' + vals[key] + ' -> ' + val);
notify.log('config change: ' + key + ': ' + oldVal + ' -> ' + newVal);
$rootScope.$broadcast('change:config.' + key, newVal, oldVal);
// reset the fire timer

View file

@ -1,42 +0,0 @@
define(function (require) {
return function RootSearchFactory(Private, config, $rootScope, timefilter, indexPatterns, Promise) {
var SearchSource = Private(require('components/courier/data_source/search_source'));
var globalSource; // the global search source - EVERYTHING inherits from this
var appSource; // the app-level search source - reset with each app change
var prom; // promise that must be resolved before the source is acurrate (updated by loadDefaultPattern)
var loadDefaultPattern = function () {
var defId = config.get('defaultIndex');
return prom = Promise.cast(defId && indexPatterns.get(defId)).then(function (pattern) {
globalSource.set('index', pattern);
return appSource;
});
};
globalSource = new SearchSource();
// searchSourceManager.registerGlobal(globalSource);
globalSource.filter(function (globalSource) {
// dynamic time filter will be called in the _flatten phase of things
return timefilter.get(globalSource.get('index'));
});
var init = function () {
appSource = new SearchSource();
appSource.inherits(globalSource);
$rootScope.$on('change:config.defaultIndex', loadDefaultPattern);
$rootScope.$on('init:config', loadDefaultPattern);
return loadDefaultPattern();
};
return function () {
return prom || init();
};
};
});

View file

@ -25,6 +25,9 @@ define(function (require) {
courier.indexPatterns = indexPatterns;
courier.redirectWhenMissing = Private(require('components/courier/_redirect_when_missing'));
courier.DocSource = DocSource;
courier.SearchSource = SearchSource;
var HastyRefresh = errors.HastyRefresh;
var Abort = errors.Abort;

View file

@ -24,8 +24,6 @@ define(function (require) {
}
}());
this._dynamicState = this._dynamicState || {};
// set internal state values
this._methods.forEach(function (name) {
this[name] = function (val) {
@ -50,13 +48,13 @@ define(function (require) {
/**
* Get values from the state
* @param {string} name - The name of the property desired
* @return {any} - the value found
*/
SourceAbstract.prototype.get = function (name) {
var current = this;
while (current) {
if (current._state[name] !== void 0) return current._state[name];
if (current._dynamicState[name] !== void 0) return current._dynamicState[name]();
current = current._parent;
current = current.getParent();
}
};
@ -116,7 +114,7 @@ define(function (require) {
* Noop
*/
SourceAbstract.prototype.getParent = function () {
return Promise.resolve(undefined);
return this._parent;
};
/**
@ -229,13 +227,12 @@ define(function (require) {
}))
.then(function () {
// move to this sources parent
return current.getParent().then(function (parent) {
// keep calling until we reach the top parent
if (parent) {
current = parent;
return ittr();
}
});
var parent = current.getParent();
// keep calling until we reach the top parent
if (parent) {
current = parent;
return ittr();
}
});
}())
.then(function () {
@ -250,8 +247,8 @@ define(function (require) {
/**
* Create a filter that can be reversed for filters with negate set
* @param {boolean} reverse This will reverse the filter. If true then
* anything where negate is set will come
* through otherwise it will filter out
* anything where negate is set will come
* through otherwise it will filter out
* @returns {function}
*/
var filterNegate = function (reverse) {

View file

@ -1,8 +1,10 @@
define(function (require) {
return function RootSearchSource(Private, $rootScope, config, Promise, indexPatterns, timefilter) {
return function RootSearchSource(Private, $rootScope, config, Promise, indexPatterns, timefilter, Notifier) {
var _ = require('lodash');
var SearchSource = Private(require('components/courier/data_source/search_source'));
var notify = new Notifier({ location: 'Root Search Source' });
var globalSource = new SearchSource();
globalSource.inherits(false); // this is the final source, it has no parents
globalSource.filter(function (globalSource) {
@ -10,13 +12,26 @@ define(function (require) {
return timefilter.get(globalSource.get('index'));
});
var ensureDefaultLoaded = _.once(__loadDefaultPattern__);
var appSource; // set in setAppSource()
resetAppSource();
// when the default index changes, or the config is intialized, connect the defaultIndex to the globalSource
$rootScope.$on('change:config.defaultIndex', ensureDefaultLoaded);
$rootScope.$on('init:config', ensureDefaultLoaded);
/**
* Get the default index from the config, and hook it up to the globalSource.
*
* @return {Promise}
*/
function loadDefaultPattern() {
return notify.event('loading default index pattern', function () {
var defId = config.get('defaultIndex');
return Promise.cast(defId && indexPatterns.get(defId))
.then(function (pattern) {
pattern = pattern || undefined;
globalSource.set('index', pattern);
notify.log('index pattern set to', defId);
});
});
}
// when the route changes, clear the appSource
$rootScope.$on('$routeChangeStart', resetAppSource);
@ -26,9 +41,7 @@ define(function (require) {
* @return {Promise} - resolved with the current AppSource
*/
function getAppSource() {
return ensureDefaultLoaded().then(function () {
return appSource;
});
return appSource;
}
/**
@ -48,19 +61,7 @@ define(function (require) {
literalRoot.inherits(globalSource);
}
/**
* Get the default index from the config, and hook it up to the globalSource. Broken out
* so that it can be called on config change.
*
* @return {Promise}
*/
function __loadDefaultPattern__() {
var defId = config.get('defaultIndex');
return Promise.cast(defId && indexPatterns.get(defId)).then(function (pattern) {
globalSource.set('index', pattern);
});
}
/**
* Sets the appSource to be a new, empty, SearchSource
@ -72,7 +73,8 @@ define(function (require) {
return {
get: getAppSource,
set: setAppSource
set: setAppSource,
loadDefault: loadDefaultPattern
};
};
});

View file

@ -6,9 +6,6 @@ define(function (require) {
var errors = require('errors');
var SourceAbstract = Private(require('components/courier/data_source/_abstract'));
var getRootSourcePromise = new Promise(function (resolve) {
require(['components/courier/data_source/_root_search_source'], _.compose(resolve, Private));
});
var FetchFailure = errors.FetchFailure;
var RequestFailure = errors.RequestFailure;
@ -18,6 +15,15 @@ define(function (require) {
}
inherits(SearchSource, SourceAbstract);
// expose a ready state for the route setup to read
var rootSearchSource;
SearchSource.ready = new Promise(function (resolve) {
require(['components/courier/data_source/_root_search_source'], function (PromiseModule) {
rootSearchSource = Private(PromiseModule);
resolve();
});
});
/*****
* PUBLIC API
*****/
@ -66,15 +72,13 @@ define(function (require) {
/**
* Get the parent of this SearchSource
* @return {Promise}
* @return {undefined|searchSource}
*/
SearchSource.prototype.getParent = function (onlyHardLinked) {
var self = this;
return getRootSourcePromise.then(function (rootSearchSource) {
if (self._parent === false) return false;
if (self._parent) return self._parent;
return onlyHardLinked ? undefined : rootSearchSource.get();
});
if (self._parent === false) return;
if (self._parent) return self._parent;
return onlyHardLinked ? undefined : rootSearchSource.get();
};
/**

View file

@ -7,7 +7,6 @@ define(function (require) {
var DocSource = Private(require('components/courier/data_source/doc_source'));
var SearchSource = Private(require('components/courier/data_source/search_source'));
var mappingSetup = Private(require('utils/mapping_setup'));
var getRootSearch = Private(require('components/courier/_get_root_search'));
function SavedObject(config) {
if (!_.isObject(config)) config = {};
@ -84,7 +83,9 @@ define(function (require) {
if (!obj.id) {
// just assign the defaults and be done
_.assign(obj, defaults);
return afterESResp.call(obj);
return hydrateIndexPattern().then(function () {
return afterESResp.call(obj);
});
}
// fetch the object from ES
@ -98,18 +99,23 @@ define(function (require) {
var meta = resp._source.kibanaSavedObjectMeta || {};
delete resp._source.kibanaSavedObjectMeta;
if (!config.indexPattern && obj._source.indexPattern) {
config.indexPattern = obj._source.indexPattern;
delete obj._source.indexPattern;
}
// assign the defaults to the response
_.defaults(resp._source, defaults);
_.defaults(obj._source, defaults);
// transform the source using _deserializers
_.forOwn(mapping, function ittr(fieldMapping, fieldName) {
if (fieldMapping._deserialize) {
resp._source[fieldName] = fieldMapping._deserialize(resp._source[fieldName], resp, fieldName, fieldMapping);
obj._source[fieldName] = fieldMapping._deserialize(obj._source[fieldName], resp, fieldName, fieldMapping);
}
});
// Give obj all of the values in _source.fields
_.assign(obj, resp._source);
_.assign(obj, obj._source);
return Promise.try(function () {
// if we have a searchSource, set it's state based on the searchSourceJSON field
@ -131,7 +137,6 @@ define(function (require) {
});
});
})
.then(hydrateIndexPattern)
.then(function () {
return customInit.call(obj);
})
@ -148,18 +153,21 @@ define(function (require) {
* @return {[type]} [description]
*/
function hydrateIndexPattern() {
if (obj.searchSource) {
var index = obj.searchSource.get('index');
return Promise.try(function () {
if (obj.searchSource) {
var index = obj.searchSource.get('index') || config.indexPattern;
if (index instanceof indexPatterns.IndexPattern) {
return;
if (!index) return;
if (index instanceof indexPatterns.IndexPattern) {
return;
}
return indexPatterns.get(index)
.then(function (indexPattern) {
obj.searchSource.index(indexPattern);
});
}
return indexPatterns.get(index)
.then(function (indexPattern) {
obj.searchSource.index(indexPattern);
});
}
});
}

View file

@ -133,9 +133,12 @@ define(function (require) {
Notifier.prototype.timed = function (name, fn) {
var self = this;
return function WrappedNotifierFunction() {
var complete = self.event(name);
fn.apply(this, arguments);
complete();
var cntx = this;
var args = arguments;
return self.event(name, function () {
return fn.apply(cntx, args);
});
};
};
@ -304,18 +307,26 @@ define(function (require) {
}
if (exec) {
if (exec.length) {
exec(complete, failure);
} else {
try {
exec();
} catch (e) {
failure(e);
return;
}
complete();
try {
ret = exec();
} catch (e) {
return failure(e);
}
if (ret && typeof ret.then === 'function') {
// return a new promise that proxies the value
// and logs about the promise outcome
return ret.then(function (val) {
complete();
return val;
}, function (err) {
failure(err);
throw err;
});
}
// the function executed fine, and didn't return a promise, move along
complete();
}
return ret;

View file

@ -29,8 +29,48 @@ define(function (require) {
var validator = function (query) {
var index, type;
if (request.abort) request.abort();
var error = function (resp) {
if ($scope.queryInput) return useSearchSource();
return useDefaults();
function useSearchSource() {
var pattern = $scope.queryInput.get('index');
if (!pattern) return useDefaults();
if (_.isString(pattern)) {
index = pattern;
}
else if (_.isFunction(pattern.toIndexList)) {
index = pattern.toIndexList();
}
else {
return useDefaults();
}
return sendRequest();
}
function useDefaults() {
index = configFile.kibanaIndex;
type = '__kibanaQueryValidator';
return sendRequest();
}
function sendRequest() {
request = es.indices.validateQuery({
index: index,
type: type,
explain: true,
ignoreUnavailable: true,
body: {
query: query || { match_all: {} }
}
}).then(success, error);
}
function error(resp) {
var msg;
ngModel.$setValidity('queryInput', false);
@ -48,9 +88,9 @@ define(function (require) {
errorElem.show();
return undefined;
};
}
var success = function (resp) {
function success(resp) {
if (resp.valid) {
ngModel.$setValidity('queryInput', true);
errorElem.hide();
@ -58,26 +98,7 @@ define(function (require) {
} else {
return error(resp);
}
};
if ($scope.queryInput) {
index = $scope.queryInput.get('index').toIndexList();
} else {
index = configFile.kibanaIndex;
type = '__kibanaQueryValidator';
}
if (request.abort) request.abort();
request = es.indices.validateQuery({
index: index,
type: type,
explain: true,
ignoreUnavailable: true,
body: {
query: query || { match_all: {} }
}
}).then(success, error);
};
var debouncedValidator = _.debounce(validator, 300, {

View file

@ -1,22 +1,28 @@
define(function (require) {
return function routeSetup(Promise, kbnSetup, config, $route, kbnUrl, indexPatterns, Notifier) {
return function routeSetup(Promise, kbnSetup, config, $route, kbnUrl, courier, Notifier, Private) {
var errors = require('errors');
var NoDefaultIndexPattern = errors.NoDefaultIndexPattern;
var NoDefinedIndexPatterns = errors.NoDefinedIndexPatterns;
var firstNoDefaultError = true;
var rootSearchSource = Private(require('components/courier/data_source/_root_search_source'));
return {
routeSetupWork: function () {
return Promise.all([
kbnSetup(),
config.init(),
courier.SearchSource.ready
])
.then(function () {
if (!$route.current.$$route.originalPath.match(/settings\/indices/)) {
return indexPatterns.getIds()
return courier.indexPatterns.getIds()
.then(function (patterns) {
if (!config.get('defaultIndex')) {
kbnUrl.change('/settings/indices');
throw new NoDefaultIndexPattern();
} else {
firstNoDefaultError = false;
return rootSearchSource.loadDefault();
}
});
}
@ -27,7 +33,14 @@ define(function (require) {
// .change short circuits the routes by calling $route.refresh(). We can safely swallow this error
// after reporting it to the user
kbnUrl.change('/settings/indices');
(new Notifier()).error(err);
if (err instanceof NoDefaultIndexPattern) {
if (firstNoDefaultError) {
firstNoDefaultError = false;
} else {
(new Notifier()).error(err);
}
}
} else {
return Promise.reject(err);
}