Merge pull request #4582 from spalger/expose/routingSetupApi
Expose/routing setup api
This commit is contained in:
commit
0a151a4b2b
|
@ -1,6 +1,7 @@
|
|||
define(function (require, module, exports) {
|
||||
var _ = require('lodash');
|
||||
|
||||
var sections = require('plugins/kibana/settings/sections/index');
|
||||
require('plugins/kibana/settings/styles/main.less');
|
||||
require('ui/filters/start_from');
|
||||
|
||||
|
@ -9,7 +10,10 @@ define(function (require, module, exports) {
|
|||
redirectTo: '/settings/indices'
|
||||
});
|
||||
|
||||
var sections = require('plugins/kibana/settings/sections/index');
|
||||
require('ui/index_patterns/routeSetup/loadDefault')({
|
||||
notRequiredRe: /^\/settings\//,
|
||||
whenMissingRedirectTo: '/settings/indices'
|
||||
});
|
||||
|
||||
require('ui/modules')
|
||||
.get('apps/settings')
|
||||
|
|
|
@ -3,6 +3,10 @@ define(function (require) {
|
|||
'kibana/notify'
|
||||
]);
|
||||
|
||||
require('ui/routes').addSetupWork(function (config) {
|
||||
return config.init();
|
||||
});
|
||||
|
||||
// service for delivering config variables to everywhere else
|
||||
module.service('config', function (Private, Notifier, kbnVersion, kbnIndex, $rootScope, buildNum) {
|
||||
var config = this;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
define(function (require) {
|
||||
return function RootSearchSource(Private, $rootScope, config, Promise, indexPatterns, timefilter, Notifier) {
|
||||
return function RootSearchSource(Private, $rootScope, timefilter, Notifier) {
|
||||
var SearchSource = Private(require('ui/courier/data_source/search_source'));
|
||||
|
||||
var notify = new Notifier({ location: 'Root Search Source' });
|
||||
|
@ -14,24 +14,6 @@ define(function (require) {
|
|||
var appSource; // set in setAppSource()
|
||||
resetAppSource();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
|
@ -73,7 +55,10 @@ define(function (require) {
|
|||
return {
|
||||
get: getAppSource,
|
||||
set: setAppSource,
|
||||
loadDefault: loadDefaultPattern
|
||||
|
||||
getGlobalSource: function () {
|
||||
return globalSource;
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
||||
|
|
|
@ -13,8 +13,6 @@ define(function (require) {
|
|||
SearchSource.Super.call(this, initialState, searchStrategy);
|
||||
}
|
||||
|
||||
// expose a ready state for the route setup to read
|
||||
|
||||
/*****
|
||||
* PUBLIC API
|
||||
*****/
|
||||
|
|
68
src/ui/public/index_patterns/routeSetup/loadDefault.js
Normal file
68
src/ui/public/index_patterns/routeSetup/loadDefault.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
let _ = require('lodash');
|
||||
let { NoDefaultIndexPattern, NoDefinedIndexPatterns } = require('ui/errors');
|
||||
let Notifier = require('ui/notify/Notifier');
|
||||
let notify = new Notifier({
|
||||
location: 'Index Patterns'
|
||||
});
|
||||
|
||||
module.exports = function (opts) {
|
||||
opts = opts || {};
|
||||
let notRequiredRe = opts.notRequiredRe || null;
|
||||
let whenMissingRedirectTo = opts.whenMissingRedirectTo || null;
|
||||
let defaultRequiredToasts = null;
|
||||
|
||||
require('ui/routes')
|
||||
.addSetupWork(function loadDefaultIndexPattern(Private, Promise, $route, config, indexPatterns) {
|
||||
let getIds = Private(require('../_get_ids'));
|
||||
let rootSearchSource = Private(require('ui/courier/data_source/_root_search_source'));
|
||||
let path = _.get($route, 'current.$$route.originalPath');
|
||||
|
||||
return config.init()
|
||||
.then(function () {
|
||||
return getIds();
|
||||
})
|
||||
.then(function (patterns) {
|
||||
let defaultId = config.get('defaultIndex');
|
||||
let defined = !!defaultId;
|
||||
let exists = _.contains(patterns, defaultId);
|
||||
let required = !notRequiredRe || !path.match(notRequiredRe);
|
||||
|
||||
if (defined && !exists) {
|
||||
config.clear('defaultIndex');
|
||||
defaultId = defined = false;
|
||||
}
|
||||
|
||||
if (!defined && required) {
|
||||
throw new NoDefaultIndexPattern();
|
||||
}
|
||||
|
||||
return notify.event('loading default index pattern', function () {
|
||||
return indexPatterns.get(defaultId).then(function (pattern) {
|
||||
rootSearchSource.getGlobalSource().set('index', pattern);
|
||||
notify.log('index pattern set to', defaultId);
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
.afterWork(
|
||||
// success
|
||||
function () {
|
||||
if (defaultRequiredToasts) {
|
||||
_.invoke(defaultRequiredToasts, 'clear');
|
||||
defaultRequiredToasts = null;
|
||||
}
|
||||
},
|
||||
|
||||
// failure
|
||||
function (err, kbnUrl) {
|
||||
let hasDefault = !(err instanceof NoDefaultIndexPattern);
|
||||
if (hasDefault || !whenMissingRedirectTo) throw err; // rethrow
|
||||
|
||||
kbnUrl.change(whenMissingRedirectTo);
|
||||
if (!defaultRequiredToasts) defaultRequiredToasts = [];
|
||||
else defaultRequiredToasts.push(notify.error(err));
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
};
|
|
@ -50,6 +50,14 @@ define(function (require) {
|
|||
return Promise.try(fn, [i, el, list]);
|
||||
}));
|
||||
};
|
||||
Promise.each = function (arr, fn) {
|
||||
let queue = arr.slice(0);
|
||||
let i = 0;
|
||||
return (function next() {
|
||||
if (!queue.length) return arr;
|
||||
return Promise.try(fn, [arr.shift(), i++]).then(next);
|
||||
}());
|
||||
};
|
||||
Promise.is = function (obj) {
|
||||
// $q doesn't create instances of any constructor, promises are just objects with a then function
|
||||
// https://github.com/angular/angular.js/blob/58f5da86645990ef984353418cd1ed83213b111e/src/ng/q.js#L335
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
var _ = require('lodash');
|
||||
|
||||
var wrapRouteWithPrep = require('./wrapRouteWithPrep');
|
||||
var RouteSetupManager = require('./RouteSetupManager');
|
||||
|
||||
function RouteManager() {
|
||||
var self = this;
|
||||
var setup = new RouteSetupManager();
|
||||
var when = [];
|
||||
var defaults = [];
|
||||
var otherwise;
|
||||
|
@ -24,16 +26,25 @@ function RouteManager() {
|
|||
route.reloadOnSearch = false;
|
||||
}
|
||||
|
||||
wrapRouteWithPrep(route);
|
||||
wrapRouteWithPrep(route, setup);
|
||||
$routeProvider.when(path, route);
|
||||
});
|
||||
|
||||
if (otherwise) {
|
||||
wrapRouteWithPrep(otherwise);
|
||||
wrapRouteWithPrep(otherwise, setup);
|
||||
$routeProvider.otherwise(otherwise);
|
||||
}
|
||||
};
|
||||
|
||||
let wrapSetupAndChain = (fn, ...args) => {
|
||||
fn.apply(setup, args);
|
||||
return this;
|
||||
};
|
||||
|
||||
this.addSetupWork = _.wrap(setup.addSetupWork, wrapSetupAndChain);
|
||||
this.afterSetupWork = _.wrap(setup.afterSetupWork, wrapSetupAndChain);
|
||||
this.afterWork = _.wrap(setup.afterWork, wrapSetupAndChain);
|
||||
|
||||
self.when = function (path, route) {
|
||||
when.push([path, route]);
|
||||
return self;
|
||||
|
|
73
src/ui/public/routes/RouteSetupManager.js
Normal file
73
src/ui/public/routes/RouteSetupManager.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
let _ = require('lodash');
|
||||
|
||||
module.exports = class RouteSetupManager {
|
||||
constructor() {
|
||||
this.setupWork = [];
|
||||
this.onSetupComplete = [];
|
||||
this.onSetupError = [];
|
||||
this.onWorkComplete = [];
|
||||
this.onWorkError = [];
|
||||
}
|
||||
|
||||
addSetupWork(fn) {
|
||||
this.setupWork.push(fn);
|
||||
}
|
||||
|
||||
afterSetupWork(onComplete, onError) {
|
||||
this.onSetupComplete.push(onComplete);
|
||||
this.onSetupError.push(onError);
|
||||
}
|
||||
|
||||
afterWork(onComplete, onError) {
|
||||
this.onWorkComplete.push(onComplete);
|
||||
this.onWorkError.push(onError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do each setupWork function by injecting it with angular dependencies
|
||||
* and accepting promises from it.
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
doWork(Promise, $injector, userWork) {
|
||||
|
||||
let invokeEach = (arr, locals) => {
|
||||
return Promise.map(arr, fn => {
|
||||
return $injector.invoke(fn, null, locals);
|
||||
});
|
||||
};
|
||||
|
||||
// call each error handler in order, until one of them resolves
|
||||
// or we run out of handlers
|
||||
let callErrorHandlers = (handlers, origError) => {
|
||||
if (!_.size(handlers)) throw origError;
|
||||
|
||||
// clone so we don't discard handlers or loose them
|
||||
handlers = handlers.slice(0);
|
||||
|
||||
let next = (err) => {
|
||||
let handler = handlers.shift();
|
||||
if (!handler) throw err;
|
||||
return $injector.invoke(handler, null, { err }).catch(next);
|
||||
};
|
||||
|
||||
return next(origError);
|
||||
};
|
||||
|
||||
return invokeEach(this.setupWork)
|
||||
.then(
|
||||
() => invokeEach(this.onSetupComplete),
|
||||
err => callErrorHandlers(this.onSetupError)
|
||||
)
|
||||
.then(() => {
|
||||
// wait for the queue to fill up, then do all the work
|
||||
var defer = Promise.defer();
|
||||
userWork.resolveWhenFull(defer);
|
||||
|
||||
return defer.promise.then(() => Promise.all(userWork.doWork()));
|
||||
})
|
||||
.then(
|
||||
() => invokeEach(this.onWorkComplete),
|
||||
err => callErrorHandlers(this.onWorkError)
|
||||
);
|
||||
}
|
||||
};
|
|
@ -39,8 +39,9 @@ describe('wrapRouteWithPrep fn', function () {
|
|||
$injector = _$injector_;
|
||||
});
|
||||
|
||||
var setup = Private(require('ui/routes/setup'));
|
||||
stub(setup, 'routeSetupWork', function () {
|
||||
|
||||
routes
|
||||
.addSetupWork(function () {
|
||||
return new Promise(function (resolve, reject) {
|
||||
setTimeout(function () {
|
||||
setupComplete = true;
|
||||
|
@ -50,17 +51,17 @@ describe('wrapRouteWithPrep fn', function () {
|
|||
});
|
||||
|
||||
routes
|
||||
.when('/', {
|
||||
resolve: {
|
||||
test: function () {
|
||||
expect(setupComplete).to.be(true);
|
||||
userWorkComplete = true;
|
||||
}
|
||||
.when('/', {
|
||||
resolve: {
|
||||
test: function () {
|
||||
expect(setupComplete).to.be(true);
|
||||
userWorkComplete = true;
|
||||
}
|
||||
})
|
||||
.config({
|
||||
when: function (p, _r) { route = _r; }
|
||||
});
|
||||
}
|
||||
})
|
||||
.config({
|
||||
when: function (p, _r) { route = _r; }
|
||||
});
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
setTimeout(function () {
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
define(function (require) {
|
||||
return function routeSetup(Promise, config, $route, kbnUrl, courier, Notifier, Private, $rootScope) {
|
||||
var _ = require('lodash');
|
||||
var errors = require('ui/errors');
|
||||
var NoDefaultIndexPattern = errors.NoDefaultIndexPattern;
|
||||
var NoDefinedIndexPatterns = errors.NoDefinedIndexPatterns;
|
||||
var firstNoDefaultError = true;
|
||||
|
||||
var rootSearchSource = Private(require('ui/courier/data_source/_root_search_source'));
|
||||
var allowedRoutesRE = /^\/settings\//;
|
||||
var notify = new Notifier();
|
||||
|
||||
return {
|
||||
routeSetupWork: function () {
|
||||
return Promise.all([
|
||||
config.init(),
|
||||
courier.SearchSource.ready,
|
||||
$rootScope.kibana && $rootScope.kibana.ready
|
||||
])
|
||||
.then(function () {
|
||||
var path = _.get($route, 'current.$$route.originalPath');
|
||||
var defaultIndexRequired = path && !path.match(allowedRoutesRE);
|
||||
|
||||
return courier.indexPatterns.getIds()
|
||||
.then(function (patterns) {
|
||||
var defined = !!config.get('defaultIndex');
|
||||
var exists = _.contains(patterns, config.get('defaultIndex'));
|
||||
|
||||
if (defined && !exists) {
|
||||
config.clear('defaultIndex');
|
||||
defined = false;
|
||||
}
|
||||
|
||||
if (!defined) {
|
||||
if (defaultIndexRequired) {
|
||||
throw new NoDefaultIndexPattern();
|
||||
} else {
|
||||
firstNoDefaultError = false;
|
||||
}
|
||||
}
|
||||
|
||||
return rootSearchSource.loadDefault();
|
||||
});
|
||||
});
|
||||
},
|
||||
handleKnownError: function (err) {
|
||||
if (err instanceof NoDefaultIndexPattern || err instanceof NoDefinedIndexPatterns) {
|
||||
// .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');
|
||||
|
||||
if (err instanceof NoDefaultIndexPattern) {
|
||||
if (firstNoDefaultError) {
|
||||
firstNoDefaultError = false;
|
||||
} else {
|
||||
notify.error(err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
|
@ -5,7 +5,7 @@ define(function (require) {
|
|||
var WorkQueue = require('ui/routes/WorkQueue');
|
||||
var errors = require('ui/errors');
|
||||
|
||||
function wrapRouteWithPrep(route) {
|
||||
function wrapRouteWithPrep(route, setup) {
|
||||
if (!route.resolve && route.redirectTo) return;
|
||||
|
||||
var userWork = new WorkQueue();
|
||||
|
@ -13,24 +13,8 @@ define(function (require) {
|
|||
userWork.limit = _.keys(route.resolve).length;
|
||||
|
||||
var resolve = {
|
||||
__prep__: function (Private, Promise, $route, $injector, Notifier) {
|
||||
var setup = Private(require('ui/routes/setup'));
|
||||
|
||||
return setup.routeSetupWork()
|
||||
.then(function () {
|
||||
// wait for the queue to fill up, then do all the work
|
||||
var defer = Promise.defer();
|
||||
userWork.resolveWhenFull(defer);
|
||||
|
||||
return defer.promise.then(function () {
|
||||
return Promise.all(userWork.doWork());
|
||||
});
|
||||
})
|
||||
.catch(function (err) {
|
||||
// discard any remaining user work
|
||||
userWork.empty();
|
||||
return setup.handleKnownError(err);
|
||||
});
|
||||
__prep__: function ($injector) {
|
||||
return $injector.invoke(setup.doWork, setup, { userWork });
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue