refactor of the notification service, it now exposes a createNotifier() factory to create a mini notification manager that will label errors with the location they originated (optionally allowing more customization of the services behavior

This commit is contained in:
Spencer Alger 2014-03-12 12:56:02 -07:00
parent d06d490405
commit 85f9912c68
5 changed files with 184 additions and 159 deletions

View file

@ -18,7 +18,10 @@ define(function (require) {
appendToBody: false
});
})
.controller('kibana', function ($scope, courier, config, configFile, notify, $timeout) {
.controller('kibana', function ($scope, courier, config, configFile, createNotifier, $timeout) {
var notify = createNotifier({
location: 'Kibana Controller'
});
$scope.apps = configFile.apps;
$scope.$on('$locationChangeSuccess', function (event, uri) {
@ -123,4 +126,4 @@ define(function (require) {
courier.start();
});
});
});
});

View file

@ -2,7 +2,6 @@
* main app level module
*/
define(function (require) {
var angular = require('angular');
var $ = require('jquery');
var _ = require('lodash');
@ -50,7 +49,7 @@ define(function (require) {
notify.lifecycle('bootstrap');
angular
.bootstrap(document, ['kibana'])
.invoke(function (notify) {
.invoke(function () {
notify.lifecycle('bootstrap', true);
$(document.body).children().show();
});
@ -61,4 +60,4 @@ define(function (require) {
});
return kibana;
});
});

View file

@ -2,6 +2,11 @@ define(function (require) {
var _ = require('lodash');
var $ = require('jquery');
var notifs = [];
var setTO = setTimeout;
var clearTO = clearTimeout;
var log = (typeof KIBANA_DIST === 'undefined') ? _.bindKey(console, 'log') : _.noop;
var fatalToastTemplate = (function lazyTemplate(tmpl) {
var compiled;
return function (vars) {
@ -10,154 +15,174 @@ define(function (require) {
};
}(require('text!./partials/fatal.html')));
/**
* Functionality to check that
*/
function NotifyManager() {
var applicationBooted;
var notifs = this._notifs = [];
var setTO = setTimeout;
var clearTO = clearTimeout;
function now() {
if (window.performance && window.performance.now) {
return window.performance.now();
}
return Date.now();
function now() {
if (window.performance && window.performance.now) {
return window.performance.now();
}
return Date.now();
}
var log = (typeof KIBANA_DIST === 'undefined') ? _.bindKey(console, 'log') : _.noop;
function closeNotif(cb, key) {
return function () {
// this === notif
var i = notifs.indexOf(this);
if (i !== -1) notifs.splice(i, 1);
if (this.timerId) this.timerId = clearTO(this.timerId);
if (typeof cb === 'function') cb(key);
};
}
function add(notif, cb) {
if (notif.lifetime !== Infinity) {
notif.timerId = setTO(function () {
closeNotif(cb, 'ignore').call(notif);
}, notif.lifetime);
}
if (notif.actions) {
notif.actions.forEach(function (action) {
notif[action] = closeNotif(cb, action);
});
}
notifs.push(notif);
}
this._setTimerFns = function (set, clear) {
setTO = set;
clearTO = clear;
};
/**
* Notify the serivce of app lifecycle events
* @type {[type]}
*/
var lifecycleEvents = window.kibanaLifecycleEvents = {};
this.lifecycle = function (name, success) {
var status;
if (name === 'bootstrap' && success === true) applicationBooted = true;
if (success === void 0) {
// start
lifecycleEvents[name] = now();
} else {
// end
if (success) {
lifecycleEvents[name] = now() - (lifecycleEvents[name] || 0);
status = lifecycleEvents[name].toFixed(2) + ' ms';
} else {
lifecycleEvents[name] = false;
status = 'failure';
}
}
log('KBN: ' + name + (status ? ' - ' + status : ''));
};
/**
* Kill the page, and display an error
* @param {Error} err - The fatal error that occured
*/
this.fatal = function (err) {
var html = fatalToastTemplate({
msg: err instanceof Error ? err.message : err,
stack: err.stack
});
var $container = $('#fatal-splash-screen');
if ($container.size()) {
$container.append(html);
return;
}
$container = $();
// in case the app has not completed boot
$(document.body)
.removeAttr('ng-cloak')
.html('<div id="fatal-splash-screen" class="container-fuild">' + html + '</div>');
};
/**
* Alert the user of an error that occured
* @param {Error|String} err
*/
this.error = function (err, cb) {
add({
type: 'danger',
content: err instanceof Error ? err.message : err,
icon: 'warning',
title: 'Error',
lifetime: Infinity,
actions: ['report', 'accept']
}, cb);
};
/**
* Warn the user abort something
* @param {[type]} msg [description]
* @return {[type]} [description]
*/
this.warning = function (msg, cb) {
add({
type: 'warning',
content: msg,
icon: 'warning',
title: 'Warning',
lifetime: 7000,
actions: ['accept']
}, cb);
};
/**
* Display a debug message
* @param {String} msg [description]
* @return {[type]} [description]
*/
this.info = function (msg, cb) {
add({
type: 'info',
content: msg,
icon: 'info-circle',
title: 'Debug',
lifetime: 7000,
actions: ['accept']
}, cb);
function closeNotif(cb, key) {
return function () {
// this === notif
var i = notifs.indexOf(this);
if (i !== -1) notifs.splice(i, 1);
if (this.timerId) this.timerId = clearTO(this.timerId);
if (typeof cb === 'function') cb(key);
};
}
return NotifyManager;
function add(notif, cb) {
if (notif.lifetime !== Infinity) {
notif.timerId = setTO(function () {
closeNotif(cb, 'ignore').call(notif);
}, notif.lifetime);
}
});
if (notif.actions) {
notif.actions.forEach(function (action) {
notif[action] = closeNotif(cb, action);
});
}
notifs.push(notif);
}
function formatMsg(msg, from) {
var rtn = '';
if (from) {
rtn += from + ': ';
}
if (typeof msg === 'string') {
rtn += msg;
} else if (msg instanceof Error) {
rtn += msg.message;
}
return rtn;
}
/**
* Track application lifecycle events
* @type {[type]}
*/
var lifecycleEvents = window.kibanaLifecycleEvents = {};
var applicationBooted;
/**
* Functionality to check that
*/
function NotifyManager(opts) {
opts = opts || {};
// label type thing to say where notifications came from
this.from = opts.location;
// attach the global notification list
this._notifs = notifs;
}
NotifyManager.prototype.lifecycle = function (name, success) {
var status;
if (name === 'bootstrap' && success === true) applicationBooted = true;
if (success === void 0) {
// start
lifecycleEvents[name] = now();
} else {
// end
if (success) {
lifecycleEvents[name] = now() - (lifecycleEvents[name] || 0);
status = lifecycleEvents[name].toFixed(2) + ' ms';
} else {
lifecycleEvents[name] = false;
status = 'failure';
}
}
log('KBN: ' + name + (status ? ' - ' + status : ''));
};
/**
* Kill the page, and display an error
* @param {Error} err - The fatal error that occured
*/
NotifyManager.prototype.fatal = function (err) {
var html = fatalToastTemplate({
msg: formatMsg(err, this.from),
stack: err.stack
});
var $container = $('#fatal-splash-screen');
if ($container.size()) {
$container.append(html);
return;
}
$container = $();
// in case the app has not completed boot
$(document.body)
.removeAttr('ng-cloak')
.html('<div id="fatal-splash-screen" class="container-fuild">' + html + '</div>');
console.error(err.stack);
};
/**
* Alert the user of an error that occured
* @param {Error|String} err
*/
NotifyManager.prototype.error = function (err, cb) {
add({
type: 'danger',
content: formatMsg(err, this.from),
icon: 'warning',
title: 'Error',
lifetime: Infinity,
actions: ['report', 'accept']
}, cb);
};
/**
* Warn the user abort something
* @param {[type]} msg [description]
* @return {[type]} [description]
*/
NotifyManager.prototype.warning = function (msg, cb) {
add({
type: 'warning',
content: formatMsg(msg, this.from),
icon: 'warning',
title: 'Warning',
lifetime: 7000,
actions: ['accept']
}, cb);
};
/**
* Display a debug message
* @param {String} msg [description]
* @return {[type]} [description]
*/
NotifyManager.prototype.info = function (msg, cb) {
add({
type: 'info',
content: formatMsg(msg),
icon: 'info-circle',
title: 'Debug',
lifetime: 7000,
actions: ['accept']
}, cb);
};
// set the timer functions that all notification managers will use
NotifyManager.prototype._setTimerFns = function (set, clear) {
setTO = set;
clearTO = clear;
};
return NotifyManager;
});

View file

@ -10,12 +10,10 @@ define(function (require) {
require('./directives');
module.service('notify', function () {
var service = this;
// modify the service to have bound proxies to the manager
_.forOwn(manager, function (val, key) {
service[key] = typeof val === 'function' ? _.bindKey(manager, key) : val;
});
module.factory('createNotifier', function () {
return function (opts) {
return new NotifyManager(opts);
};
});
/**
@ -90,4 +88,4 @@ define(function (require) {
return manager;
});
});

View file

@ -37,7 +37,7 @@ define(function (require) {
angular
.bootstrap(appEl, ['setup'])
.invoke(function (es, config, notify) {
.invoke(function (es, config) {
// init the setup module
async.series([
async.apply(checkForES, es),
@ -136,4 +136,4 @@ define(function (require) {
});
});
};
});
});