Merge pull request #4914 from epixa/4899-error-build

Render version and build number in fatal errors
This commit is contained in:
Jonathan Budzenski 2015-09-14 14:42:38 -05:00
commit 076717597a
7 changed files with 234 additions and 1 deletions

View file

@ -21,6 +21,8 @@ ${pluginSlug}
*/
window.__KBN__ = {
version: '1.2.3',
buildNum: 1234,
vars: {
kbnIndex: '.kibana',
esShardTimeout: 1500,

View file

@ -0,0 +1,17 @@
describe('ui/metadata', () => {
const expect = require('expect.js');
const metadata = require('ui/metadata');
it('is same data as window.__KBN__', () => {
expect(metadata.version).to.equal(window.__KBN__.version);
expect(metadata.vars.kbnIndex).to.equal(window.__KBN__.vars.kbnIndex);
});
it('is immutable', () => {
expect(() => metadata.foo = 'something').to.throw;
expect(() => metadata.version = 'something').to.throw;
expect(() => metadata.vars = {}).to.throw;
expect(() => metadata.vars.kbnIndex = 'something').to.throw;
});
});

View file

@ -8,6 +8,7 @@ require('ui/timefilter');
require('ui/private');
require('ui/promises');
var metadata = require('ui/metadata');
var TabCollection = require('ui/chrome/TabCollection');
var chrome = {
@ -17,7 +18,7 @@ var chrome = {
};
var internals = _.assign(
_.cloneDeep(window.__KBN__ || {}),
_.cloneDeep(metadata),
{
tabs: new TabCollection(),
rootController: null,

24
src/ui/public/metadata.js Normal file
View file

@ -0,0 +1,24 @@
// singleton for immutable copy of window.__KBN__
define(function (require) {
const _ = require('lodash');
if (!_.has(window, '__KBN__')) {
throw new Error('window.__KBN__ must be set for metadata');
}
const kbn = _.cloneDeep(window.__KBN__ || {});
return deepFreeze(kbn);
function deepFreeze(object) {
// for any properties that reference an object, makes sure that object is
// recursively frozen as well
Object.keys(object).forEach(key => {
const value = object[key];
if (_.isObject(value)) {
deepFreeze(value);
}
});
return Object.freeze(object);
}
});

View file

@ -3,9 +3,13 @@ define(function (require) {
var _ = require('lodash');
var $ = require('jquery');
var metadata = require('ui/metadata');
var notifs = [];
var setTO = setTimeout;
var clearTO = clearTimeout;
var version = metadata.version;
var buildNum = metadata.buildNum;
var consoleGroups = ('group' in window.console) && ('groupCollapsed' in window.console) && ('groupEnd' in window.console);
var fatalSplashScreen = require('ui/notify/partials/fatal_splash_screen.html');
@ -40,6 +44,9 @@ define(function (require) {
}
function add(notif, cb) {
_.set(notif, 'info.version', version);
_.set(notif, 'info.buildNum', buildNum);
if (notif.lifetime !== Infinity) {
notif.timerId = setTO(function () {
closeNotif(cb, 'ignore').call(notif);
@ -85,6 +92,20 @@ define(function (require) {
return rtn;
}
function formatInfo() {
var info = [];
if (!_.isUndefined(version)) {
info.push(`Version: ${version}`);
}
if (!_.isUndefined(buildNum)) {
info.push(`Build: ${buildNum}`);
}
return info.join('\n');
}
// browsers format Error.stack differently; always include message
function formatStack(err) {
if (err.stack && !~err.stack.indexOf(err.message)) {
@ -192,6 +213,7 @@ define(function (require) {
}
var html = fatalToastTemplate({
info: formatInfo(),
msg: formatMsg(err, this.from),
stack: formatStack(err)
});

View file

@ -0,0 +1,164 @@
describe('Notifier', function () {
var _ = require('lodash');
var ngMock = require('ngMock');
var expect = require('expect.js');
var Notifier = require('ui/notify/Notifier');
var message = 'Oh, the humanity!';
var notifier;
var params;
var version = window.__KBN__.version;
var buildNum = window.__KBN__.buildNum;
beforeEach(ngMock.module('kibana'));
beforeEach(function () {
params = { location: 'foo' };
while (Notifier.prototype._notifs.pop()); // clear global notifications
notifier = new Notifier(params);
});
describe('#constructor()', function () {
it('sets #from from given location', function () {
expect(notifier.from).to.equal(params.location);
});
});
describe('#error', function () {
testVersionInfo('error');
it('prepends location to message for content', function () {
expect(notify('error').content).to.equal(params.location + ': ' + message);
});
it('sets type to "danger"', function () {
expect(notify('error').type).to.equal('danger');
});
it('sets icon to "warning"', function () {
expect(notify('error').icon).to.equal('warning');
});
it('sets title to "Error"', function () {
expect(notify('error').title).to.equal('Error');
});
it('sets lifetime to Infinity', function () {
expect(notify('error').lifetime).to.equal(Infinity);
});
it('allows reporting', function () {
var includesReport = _.includes(notify('error').actions, 'report');
expect(includesReport).to.true;
});
it('allows accepting', function () {
var includesAccept = _.includes(notify('error').actions, 'accept');
expect(includesAccept).to.true;
});
it('includes stack', function () {
expect(notify('error').stack).to.be.defined;
});
});
describe('#warning', function () {
testVersionInfo('warning');
it('prepends location to message for content', function () {
expect(notify('warning').content).to.equal(params.location + ': ' + message);
});
it('sets type to "warning"', function () {
expect(notify('warning').type).to.equal('warning');
});
it('sets icon to "warning"', function () {
expect(notify('warning').icon).to.equal('warning');
});
it('sets title to "Warning"', function () {
expect(notify('warning').title).to.equal('Warning');
});
it('sets lifetime to 10000', function () {
expect(notify('warning').lifetime).to.equal(10000);
});
it('does not allow reporting', function () {
var includesReport = _.includes(notify('warning').actions, 'report');
expect(includesReport).to.false;
});
it('allows accepting', function () {
var includesAccept = _.includes(notify('warning').actions, 'accept');
expect(includesAccept).to.true;
});
it('does not include stack', function () {
expect(notify('warning').stack).not.to.be.defined;
});
});
describe('#info', function () {
testVersionInfo('info');
it('prepends location to message for content', function () {
expect(notify('info').content).to.equal(params.location + ': ' + message);
});
it('sets type to "info"', function () {
expect(notify('info').type).to.equal('info');
});
it('sets icon to "info-circle"', function () {
expect(notify('info').icon).to.equal('info-circle');
});
it('sets title to "Debug"', function () {
expect(notify('info').title).to.equal('Debug');
});
it('sets lifetime to 5000', function () {
expect(notify('info').lifetime).to.equal(5000);
});
it('does not allow reporting', function () {
var includesReport = _.includes(notify('info').actions, 'report');
expect(includesReport).to.false;
});
it('allows accepting', function () {
var includesAccept = _.includes(notify('info').actions, 'accept');
expect(includesAccept).to.true;
});
it('does not include stack', function () {
expect(notify('info').stack).not.to.be.defined;
});
});
function notify(fnName) {
notifier[fnName](message);
return latestNotification();
}
function latestNotification() {
return _.last(notifier._notifs);
}
function testVersionInfo(fnName) {
context('when version is configured', function () {
it('adds version to notification', function () {
var notification = notify(fnName);
expect(notification.info.version).to.equal(version);
});
});
context('when build number is configured', function () {
it('adds buildNum to notification', function () {
var notification = notify(fnName);
expect(notification.info.buildNum).to.equal(buildNum);
});
});
}
});

View file

@ -12,6 +12,9 @@
</h1>
</div>
<div class="panel-body fatal-body"><%- msg %></div>
<% if (info) { %>
<div class="panel-footer"><pre><%- info %></pre></div>
<% } %>
<% if (stack) { %>
<div class="panel-footer"><pre><%- stack %></pre></div>
<% } %>