Merge branch 'master' into addTileMapTests

This commit is contained in:
LeeDr 2016-08-11 12:42:52 -05:00
commit 409ec9d55c
41 changed files with 448 additions and 183 deletions

View file

@ -1,4 +1,4 @@
# Kibana 5.0.0-alpha5
# Kibana 5.0.0-alpha6
Kibana is an open source ([Apache Licensed](https://github.com/elastic/kibana/blob/master/LICENSE.md)), browser based analytics and search dashboard for Elasticsearch. Kibana is a snap to setup and start using. Kibana strives to be easy to get started with, while also being flexible and powerful, just like Elasticsearch.
@ -43,7 +43,7 @@ For the daring, snapshot builds are available. These builds are created after ea
| platform | |
| --- | --- |
| OSX | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-alpha5-SNAPSHOT-darwin-x86_64.tar.gz) |
| Linux x64 | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-alpha5-SNAPSHOT-linux-x86_64.tar.gz) [deb](https://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-alpha5-SNAPSHOT-amd64.deb) [rpm](https://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-alpha5-SNAPSHOT-x86_64.rpm) |
| Linux x86 | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-alpha5-SNAPSHOT-linux-x86.tar.gz) [deb](https://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-alpha5-SNAPSHOT-i386.deb) [rpm](https://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-alpha5-SNAPSHOT-i686.rpm) |
| Windows | [zip](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-alpha5-SNAPSHOT-windows-x86.zip) |
| OSX | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-alpha6-SNAPSHOT-darwin-x86_64.tar.gz) |
| Linux x64 | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-alpha6-SNAPSHOT-linux-x86_64.tar.gz) [deb](https://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-alpha6-SNAPSHOT-amd64.deb) [rpm](https://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-alpha6-SNAPSHOT-x86_64.rpm) |
| Linux x86 | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-alpha6-SNAPSHOT-linux-x86.tar.gz) [deb](https://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-alpha6-SNAPSHOT-i386.deb) [rpm](https://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-alpha6-SNAPSHOT-i686.rpm) |
| Windows | [zip](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-alpha6-SNAPSHOT-windows-x86.zip) |

View file

@ -1,5 +1,5 @@
[[setup-repositories]]
=== Kibana Repositories
=== Install Kibana using a Linux Package Manager
Binary packages for Kibana are available for Unix distributions that support the `apt` and `yum` tools. We also have
repositories available for APT and YUM based distributions.

View file

@ -1,10 +1,10 @@
[[setup]]
== Getting Kibana Up and Running
== Installing Kibana
You can set up Kibana and start exploring your Elasticsearch indices in minutes.
All you need is:
* Elasticsearch {esversion}
* A modern web browser - http://www.elastic.co/subscriptions/matrix#matrix_browsers[Supported Browsers].
* A modern web browser - https://www.elastic.co/support/matrix#show_browsers[Supported Browsers].
* Information about your Elasticsearch installation:
** URL of the Elasticsearch instance you want to connect to.
** Which Elasticsearch indices you want to search.
@ -12,15 +12,40 @@ All you need is:
NOTE: If your Elasticsearch installation is protected by http://www.elastic.co/overview/shield/[{scyld}], see
{shield}/kibana.html#using-kibana4-with-shield[{scyld} with Kibana] for additional setup instructions.
=== Upgrading Kibana
Your existing Kibana version is generally compatible with the next minor version release of Elasticsearch.
This means you should upgrade your Elasticsearch cluster(s) before or at the same time as Kibana.
We cannot guarantee compatibility between major version releases so in those cases both Elasticsearch and Kibana must be upgraded together.
To upgrade Kibana:
. Create a https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html[snapshot] of the existing `.kibana` index.
. Back up the `kibana.yml` configuration file.
. Take note of the Kibana plugins that are installed:
* `bin/kibana plugin --list` on 4.x versions of Kibana.
* `bin/kibana-plugin list` on 5.x versions of Kibana.
. To upgrade from an Archive File:
.. Extract the new version of Kibana into a different directory. See steps below.
.. Migrate any custom configuration from your old kibana.yml to your new one
.. Follow other steps below to complete the new installation.
.. Once the new version is fully configured and working with required plugins, remove the previous version of Kibana
. To upgrade using a Linux Package Manager:
.. Uninstall the existing Kibana package: `apt-get remove kibana` or `yum remove kibana`
.. Install the new Kibana package. There have been some installer issues between various version of Kibana so the uninstall and install process is safer than an upgrade.
[float]
[[install]]
=== Install and Start Kibana
=== Install and Start Kibana from an Archive File
To get Kibana up and running:
. Download the https://www.elastic.co/downloads/kibana[Kibana {version} binary package] for your platform.
. Extract the `.zip` or `tar.gz` archive file.
. After installing, run Kibana from the install directory: `bin/kibana` (Linux/MacOSX) or `bin\kibana.bat` (Windows).
. If you're upgrading, migrate any configuration changes from the previous `kibana.yml` to the new version.
. Install Kibana plugins (optional).
. Run Kibana from the install directory: `bin/kibana` (Linux/MacOSX) or `bin\kibana.bat` (Windows).
On Unix, you can instead run the package manager suited for your distribution.

View file

@ -11,7 +11,7 @@
"dashboarding"
],
"private": false,
"version": "5.0.0-alpha5",
"version": "5.0.0-alpha6",
"build": {
"number": 8467,
"sha": "6cb7fec4e154faa0a4a3fee4b33dfef91b9870d9"

View file

@ -426,6 +426,12 @@ var rules = {
script: {
// populated by a global rule
}
},
"matrix_stats": {
__template: {
fields: []
},
fields: ["{field}"]
}
}
};

View file

@ -93,11 +93,23 @@ module.exports = function (api) {
],
url_params: {
"analyzer": "",
"char_filters": [],
"char_filter": [],
"field": "",
"filters": [],
"filter": [],
"text": "",
"tokenizer": ""
"tokenizer": "",
"explain": "__flag__",
"attributes": []
},
data_autocomplete_rules: {
text: [],
field: "{field}",
analyzer: "",
tokenizer: "",
char_filter: [],
filter: [],
explain: {__one_of: [false, true]},
attributes: []
}
});

View file

@ -18,9 +18,20 @@ module.exports.send = function (method, path, data, server, disable_auth_alert)
// delayed loading for circular references
var settings = require("./settings");
let contentType;
if (data) {
try {
JSON.parse(data);
contentType = 'application/json';
} catch (e) {
contentType = 'text/plain';
}
}
var options = {
url: '../api/console/proxy?uri=' + encodeURIComponent(path),
data: method == "GET" ? null : data,
contentType,
cache: false,
crossDomain: true,
type: method,

View file

@ -4,6 +4,10 @@
Bar Mode
</label>
<select class="form-control" ng-model="vis.params.mode" ng-options="mode for mode in vis.type.params.modes"></select>
<label>
Y-Axis Scale
</label>
<select class="form-control" ng-model="vis.params.scale" ng-options="mode for mode in vis.type.params.scales"></select>
</div>
<point-series-options></point-series-options>
<vislib-basic-options></vislib-basic-options>

View file

@ -83,6 +83,7 @@ uiModules.get('apps/management')
ingest.delete($scope.indexPattern.id)
.then($scope.indexPattern.destroy.bind($scope.indexPattern))
.then(refreshKibanaIndex)
.then(function () {
$location.url('/management/data/index');
})

View file

@ -86,7 +86,8 @@ app.service('savedVisualizations', function (Promise, es, kbnIndex, SavedVis, Pr
simple_query_string: {
query: searchString + '*',
fields: ['title^3', 'description'],
default_operator: 'AND'
default_operator: 'AND',
analyze_wildcard: true
}
}
};

View file

@ -1,4 +1,3 @@
import Promise from 'bluebird';
import Joi from 'joi';
import _ from 'lodash';
import override from './override';

View file

@ -1,6 +1,5 @@
import _ from 'lodash';
module.exports = function (dot, nestedObj, flattenArrays) {
let key; // original key
let stack = []; // track key stack
let flatObj = {};
(function flattenObj(obj) {

View file

@ -1,6 +1,4 @@
import Joi from 'joi';
import fs from 'fs';
import path from 'path';
import { get } from 'lodash';
import { randomBytes } from 'crypto';
import os from 'os';

View file

@ -127,8 +127,13 @@ module.exports = class Plugin {
server.exposeStaticDir(`/plugins/${id}/{path*}`, this.publicDir);
}
this.status = kbnServer.status.createForPlugin(this);
server.expose('status', this.status);
// Many of the plugins are simply adding static assets to the server and we don't need
// to track their "status". Since plugins must have an init() function to even set its status
// we shouldn't even create a status unless the plugin can use it.
if (this.externalInit !== _.noop) {
this.status = kbnServer.status.createForPlugin(this);
server.expose('status', this.status);
}
return await attempt(this.externalInit, [server, options], this);
};
@ -148,7 +153,7 @@ module.exports = class Plugin {
// Only change the plugin status to green if the
// intial status has not been changed
if (this.status.state === 'uninitialized') {
if (this.status && this.status.state === 'uninitialized') {
this.status.green('Ready');
}
}

View file

@ -24,6 +24,14 @@ function KbnError(msg, constructor) {
errors.KbnError = KbnError;
_.class(KbnError).inherits(Error);
/**
* If the error permits, propagate the error to be rendered on screen
* @param handler the handlers that can render the error message to the screen.
*/
KbnError.prototype.displayToScreen = function (handler) {
throw this;
};
/**
* HastyRefresh error class
* @param {String} [msg] - An error message that will probably end up in a log.
@ -204,71 +212,81 @@ errors.NoDefaultIndexPattern = function NoDefaultIndexPattern(type) {
_.class(errors.NoDefaultIndexPattern).inherits(KbnError);
/**
* used by the vislib, when the container is too small
* @param {String} message - the message to provide with the error
*/
errors.ContainerTooSmall = function ContainerTooSmall() {
errors.PersistedStateError = function PersistedStateError(msg) {
KbnError.call(this,
'This container is too small to render the visualization',
errors.ContainerTooSmall);
};
_.class(errors.ContainerTooSmall).inherits(KbnError);
_.class(errors.PersistedStateError).inherits(KbnError);
/**
* UI Errors
*/
errors.VislibError = class VislibError extends KbnError {
constructor(message) {
super(message);
}
displayToScreen(handler) {
handler.error(this.message);
}
};
errors.ContainerTooSmall = class ContainerTooSmall extends errors.VislibError {
constructor() {
super('This container is too small to render the visualization');
}
};
errors.InvalidWiggleSelection = class InvalidWiggleSelection extends errors.VislibError {
constructor() {
super('In wiggle mode the area chart requires ordered values on the x-axis. Try using a Histogram or Date Histogram aggregation.');
}
};
errors.PieContainsAllZeros = class PieContainsAllZeros extends errors.VislibError {
constructor() {
super('No results displayed because all values equal 0.');
}
};
errors.InvalidLogScaleValues = class InvalidLogScaleValues extends errors.VislibError {
constructor() {
super('Values less than 1 cannot be displayed on a log scale');
}
};
errors.StackedBarChartConfig = class StackedBarChartConfig extends errors.VislibError {
constructor(message) {
super(message);
}
};
/**
* error thrown when user tries to render an chart with less
* than the required number of data points
* @param {String} message - the message to provide with the error
*/
errors.NotEnoughData = function NotEnoughData(message) {
KbnError.call(this, message, errors.NotEnoughData);
errors.NotEnoughData = class NotEnoughData extends errors.VislibError {
constructor(message) {
super(message);
}
};
_.class(errors.NotEnoughData).inherits(KbnError);
/**
* error thrown when no results are returned from an elasticsearch query
*/
errors.NoResults = function NoResults() {
KbnError.call(this,
'No results found',
errors.NoResults);
errors.NoResults = class NoResults extends errors.VislibError {
constructor() {
super('No results found');
}
};
_.class(errors.NoResults).inherits(KbnError);
/**
* error thrown when no results are returned from an elasticsearch query
*/
errors.PieContainsAllZeros = function PieContainsAllZeros() {
KbnError.call(this,
'No results displayed because all values equal 0',
errors.PieContainsAllZeros);
};
_.class(errors.PieContainsAllZeros).inherits(KbnError);
/**
* error thrown when no results are returned from an elasticsearch query
*/
errors.InvalidLogScaleValues = function InvalidLogScaleValues() {
KbnError.call(this,
'Values less than 1 cannot be displayed on a log scale',
errors.InvalidLogScaleValues);
};
_.class(errors.InvalidLogScaleValues).inherits(KbnError);
/** error thrown when wiggle chart is selected for non linear data */
errors.InvalidWiggleSelection = function InvalidWiggleSelection() {
KbnError.call(this,
'In wiggle mode the area chart requires ordered values on the x-axis. Try using a Histogram or Date Histogram aggregation.',
errors.InvalidWiggleSelection);
};
_.class(errors.InvalidWiggleSelection).inherits(KbnError);
errors.PersistedStateError = function PersistedStateError(msg) {
KbnError.call(this,
msg || 'PersistedState Error',
errors.PersistedStateError);
};
_.class(errors.PersistedStateError).inherits(KbnError);
export default errors;

View file

@ -66,6 +66,10 @@ describe('Notifier', function () {
expect(notify('error').lifetime).to.equal(300000);
});
it('sets truncation length to 250', function () {
expect(notify('error').truncationLength).to.equal(250);
});
it('sets timeRemaining and decrements', function () {
let notif = notify('error');
@ -143,6 +147,10 @@ describe('Notifier', function () {
expect(notify('warning').lifetime).to.equal(10000);
});
it('sets truncation length to 250', function () {
expect(notify('warning').truncationLength).to.equal(250);
});
it('does not allow reporting', function () {
let includesReport = _.includes(notify('warning').actions, 'report');
expect(includesReport).to.false;
@ -181,6 +189,10 @@ describe('Notifier', function () {
expect(notify('info').lifetime).to.equal(5000);
});
it('sets truncation length to 250', function () {
expect(notify('info').truncationLength).to.equal(250);
});
it('does not allow reporting', function () {
let includesReport = _.includes(notify('info').actions, 'report');
expect(includesReport).to.false;
@ -229,16 +241,18 @@ describe('Notifier', function () {
// destroy the default custom notification, avoid duplicate handling
customNotification.clear();
const explicitLifetimeParams = _.defaults({ lifetime: 20000 }, customParams);
customNotification = notifier.custom(customText, explicitLifetimeParams);
const overrideParams = _.defaults({ lifetime: 20000, truncationLength: 1000 }, customParams);
customNotification = notifier.custom(customText, overrideParams);
expect(customNotification).to.have.property('type', 'info'); // default
expect(customNotification).to.have.property('title', explicitLifetimeParams.title); // passed in
expect(customNotification).to.have.property('lifetime', explicitLifetimeParams.lifetime); // passed in
expect(customNotification).to.have.property('title', overrideParams.title); // passed in thru customParams
expect(customNotification).to.have.property('truncationLength', overrideParams.truncationLength); // passed in thru overrideParams
expect(customNotification).to.have.property('lifetime', overrideParams.lifetime); // passed in thru overrideParams
expect(explicitLifetimeParams.type).to.be(undefined);
expect(explicitLifetimeParams.title).to.be.a('string');
expect(explicitLifetimeParams.lifetime).to.be.a('number');
expect(overrideParams.type).to.be(undefined);
expect(overrideParams.title).to.be.a('string');
expect(overrideParams.truncationLength).to.be.a('number');
expect(overrideParams.lifetime).to.be.a('number');
});
it('sets the content', function () {
@ -340,6 +354,10 @@ describe('Notifier', function () {
expect(notify('banner').title).to.equal('Attention');
});
it('sets truncation length to 250 by default', function () {
expect(notify('banner').truncationLength).to.equal(250);
});
it('sets lifetime to 3000000 by default', function () {
expect(notify('banner').lifetime).to.equal(3000000);
});

View file

@ -188,7 +188,8 @@ Notifier.config = {
warningLifetime: 10000,
infoLifetime: 5000,
setInterval: window.setInterval,
clearInterval: window.clearInterval
clearInterval: window.clearInterval,
defaultTruncationLength: 250
};
Notifier.applyConfig = function (config) {
@ -331,6 +332,7 @@ Notifier.prototype.error = function (err, opts, cb) {
const config = _.assign({
type: 'danger',
content: formatMsg(err, this.from),
truncationLength: Notifier.config.defaultTruncationLength,
icon: 'warning',
title: 'Error',
lifetime: Notifier.config.errorLifetime,
@ -354,6 +356,7 @@ Notifier.prototype.warning = function (msg, opts, cb) {
const config = _.assign({
type: 'warning',
content: formatMsg(msg, this.from),
truncationLength: Notifier.config.defaultTruncationLength,
icon: 'warning',
title: 'Warning',
lifetime: Notifier.config.warningLifetime,
@ -376,6 +379,7 @@ Notifier.prototype.info = function (msg, opts, cb) {
const config = _.assign({
type: 'info',
content: formatMsg(msg, this.from),
truncationLength: Notifier.config.defaultTruncationLength,
icon: 'info-circle',
title: 'Debug',
lifetime: Notifier.config.infoLifetime,
@ -394,6 +398,7 @@ Notifier.prototype.banner = function (msg, cb) {
type: 'banner',
title: 'Attention',
content: formatMsg(msg, this.from),
truncationLength: Notifier.config.defaultTruncationLength,
lifetime: Notifier.config.bannerLifetime,
actions: ['accept']
}, cb);
@ -448,6 +453,7 @@ Notifier.prototype.custom = function (msg, config, cb) {
type: 'info',
title: 'Notification',
content: formatMsg(msg, this.from),
truncationLength: config.truncationLength || Notifier.config.defaultTruncationLength,
lifetime: getLifetime(config.type)
}, config);

View file

@ -7,7 +7,7 @@
<i class="fa" ng-class="'fa-' + notif.icon" tooltip="{{notif.title}}"></i>
<kbn-truncated source="{{notif.content | markdown}}" is-html="true" length="250" class="toast-message" /></kbn-truncated>
<kbn-truncated source="{{notif.content | markdown}}" is-html="true" length="{{notif.truncationLength}}" class="toast-message" /></kbn-truncated>
<div class="btn-group pull-right toast-controls">
<button

View file

@ -38,7 +38,7 @@ uiModules
template: function ($el, $attrs) {
return $el.html();
},
controller: function ($scope, $element, $attrs, $transclude) {
controller: function ($scope, $element, $attrs, $transclude, $injector) {
if (!$scope.definition) throw new Error('render-directive must have a definition attribute');
const { controller, controllerAs, scope } = $scope.definition;
@ -46,8 +46,16 @@ uiModules
applyScopeBindings(scope, $scope, $attrs);
if (controller) {
if (controllerAs) $scope[controllerAs] = this;
$scope.$eval(controller, { $scope, $element, $attrs, $transclude });
if (controllerAs) {
$scope[controllerAs] = this;
}
const locals = { $scope, $element, $attrs, $transclude };
const controllerInstance = $injector.invoke(controller, this, locals) || this;
if (controllerAs) {
$scope[controllerAs] = controllerInstance;
}
}
},
link: {

View file

@ -4,7 +4,7 @@ import StateManagementStateProvider from 'ui/state_management/state';
import PersistedStatePersistedStateProvider from 'ui/persisted_state/persisted_state';
let urlParam = '_a';
function AppStateProvider(Private, $rootScope, getAppState) {
function AppStateProvider(Private, $rootScope, $location) {
let State = Private(StateManagementStateProvider);
let PersistedState = Private(PersistedStatePersistedStateProvider);
let persistedStates;
@ -16,7 +16,7 @@ function AppStateProvider(Private, $rootScope, getAppState) {
eventUnsubscribers = [];
AppState.Super.call(this, urlParam, defaults);
getAppState._set(this);
AppState.getAppState._set(this);
}
// if the url param is missing, write it back
@ -24,7 +24,7 @@ function AppStateProvider(Private, $rootScope, getAppState) {
AppState.prototype.destroy = function () {
AppState.Super.prototype.destroy.call(this);
getAppState._set(null);
AppState.getAppState._set(null);
_.callEach(eventUnsubscribers);
};
@ -64,6 +64,26 @@ function AppStateProvider(Private, $rootScope, getAppState) {
return persistedStates[prop];
};
AppState.getAppState = (function () {
let currentAppState;
function get() {
return currentAppState;
}
// Checks to see if the appState might already exist, even if it hasn't been newed up
get.previouslyStored = function () {
let search = $location.search();
return search[urlParam] ? true : false;
};
get._set = function (current) {
currentAppState = current;
};
return get;
}());
return AppState;
}
@ -71,24 +91,8 @@ modules.get('kibana/global_state')
.factory('AppState', function (Private) {
return Private(AppStateProvider);
})
.service('getAppState', function ($location) {
let currentAppState;
function get() {
return currentAppState;
}
// Checks to see if the appState might already exist, even if it hasn't been newed up
get.previouslyStored = function () {
let search = $location.search();
return search[urlParam] ? true : false;
};
get._set = function (current) {
currentAppState = current;
};
return get;
.service('getAppState', function (Private) {
return Private(AppStateProvider).getAppState;
});
export default AppStateProvider;

View file

@ -7,7 +7,7 @@ import uiModules from 'ui/modules';
let module = uiModules.get('kibana/global_state');
module.service('globalState', function (Private, $rootScope, $location) {
function GlobalStateProvider(Private, $rootScope, $location) {
let State = Private(StateManagementStateProvider);
_.class(GlobalState).inherits(State);
@ -23,4 +23,9 @@ module.service('globalState', function (Private, $rootScope, $location) {
};
return new GlobalState();
}
module.service('globalState', function (Private) {
return Private(GlobalStateProvider);
});
export default GlobalStateProvider;

View file

@ -0,0 +1,58 @@
import expect from 'expect.js';
import ngMock from 'ng_mock';
import RegistryFieldFormatsProvider from 'ui/registry/field_formats';
describe('Boolean Format', function () {
let boolean;
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private) {
const fieldFormats = Private(RegistryFieldFormatsProvider);
boolean = fieldFormats.getInstance('boolean');
}));
[
{
input: 0,
expected: 'false'
},
{
input: 'no',
expected: 'false'
},
{
input: false,
expected: 'false'
},
{
input: 'false',
expected: 'false'
},
{
input: 1,
expected: 'true'
},
{
input: 'yes',
expected: 'true'
},
{
input: true,
expected: 'true'
},
{
input: 'true',
expected: 'true'
},
{
input: ' True ',//should handle trailing and mixed case
expected: 'true'
}
].forEach((test)=> {
it(`convert ${test.input} to boolean`, ()=> {
expect(boolean.convert(test.input)).to.be(test.expected);
});
});
});

View file

@ -19,10 +19,12 @@ const formatIds = [
'string',
'url',
'_source',
'truncate'
'truncate',
'boolean'
];
module.exports = describe('conformance', function () {
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private, $injector) {
fieldFormats = Private(RegistryFieldFormatsProvider);

View file

@ -7,5 +7,6 @@ import './_color';
import './_date';
import './_duration';
import './_truncate';
import './_boolean';
describe('Stringify Component', function () {
});

View file

@ -10,6 +10,7 @@ import stringifyString from 'ui/stringify/types/string';
import stringifySource from 'ui/stringify/types/source';
import stringifyColor from 'ui/stringify/types/color';
import stringifyTruncate from 'ui/stringify/types/truncate';
import stringifyBoolean from 'ui/stringify/types/boolean';
fieldFormats.register(stringifyUrl);
fieldFormats.register(stringifyBytes);
@ -22,3 +23,4 @@ fieldFormats.register(stringifyString);
fieldFormats.register(stringifySource);
fieldFormats.register(stringifyColor);
fieldFormats.register(stringifyTruncate);
fieldFormats.register(stringifyBoolean);

View file

@ -0,0 +1,42 @@
import IndexPatternsFieldFormatProvider from 'ui/index_patterns/_field_format/field_format';
import _ from 'lodash';
export default function TruncateFormatProvider(Private) {
let FieldFormat = Private(IndexPatternsFieldFormatProvider);
class Bool extends FieldFormat {
constructor(params) {
super(params);
}
_convert(value) {
if (typeof value === 'string') {
value = value.trim().toLowerCase();
}
switch (value) {
case false:
case 0:
case 'false':
case 'no':
return 'false';
case true:
case 1:
case 'true':
case 'yes':
return 'true';
default:
return _.asPrettyString(value);
}
}
}
Bool.id = 'boolean';
Bool.title = 'Boolean';
Bool.fieldType = ['boolean', 'number', 'string'];
return Bool;
};

View file

@ -5,6 +5,7 @@ import ngMock from 'ng_mock';
import faker from 'faker';
import _ from 'lodash';
import MockState from 'fixtures/mock_state';
import AppStateProvider from 'ui/state_management/app_state';
import 'ui/url';
// global vars, injected and mocked in init()
@ -12,12 +13,11 @@ let kbnUrl;
let $route;
let $location;
let $rootScope;
let globalStateMock;
let appState;
function init() {
ngMock.module('kibana/url', 'kibana', function ($provide) {
ngMock.module('kibana/url', 'kibana', function ($provide, PrivateProvider) {
$provide.service('$route', function () {
return {
reload: _.noop
@ -25,19 +25,10 @@ function init() {
});
appState = { destroy: sinon.stub() };
$provide.service('getAppState', function () {
return function () {
return appState;
};
});
$provide.service('globalState', function () {
globalStateMock = new MockState();
globalStateMock.removeFromUrl = function (url) {
return url;
};
return globalStateMock;
PrivateProvider.swap(AppStateProvider, $decorate => {
const AppState = $decorate();
AppState.getAppState = () => appState;
return AppState;
});
});
@ -433,7 +424,7 @@ describe('kbnUrl', function () {
it('returns false if the passed url doesn\'t match the current route', function () {
next.path = '/not current';
expect(kbnUrl._shouldAutoReload(next, prev)).to.be(false);
expect(kbnUrl._shouldAutoReload(next, prev, $route)).to.be(false);
});
describe('if the passed url does match the route', function () {
@ -441,14 +432,14 @@ describe('kbnUrl', function () {
describe('and the path is the same', function () {
describe('and the search params are the same', function () {
it('returns true', function () {
expect(kbnUrl._shouldAutoReload(next, prev)).to.be(true);
expect(kbnUrl._shouldAutoReload(next, prev, $route)).to.be(true);
});
});
describe('but the search params are different', function () {
it('returns false', function () {
next.search = {};
prev.search = { q: 'search term' };
expect(kbnUrl._shouldAutoReload(next, prev)).to.be(false);
expect(kbnUrl._shouldAutoReload(next, prev, $route)).to.be(false);
});
});
});
@ -460,14 +451,14 @@ describe('kbnUrl', function () {
describe('and the search params are the same', function () {
it('returns false', function () {
expect(kbnUrl._shouldAutoReload(next, prev)).to.be(false);
expect(kbnUrl._shouldAutoReload(next, prev, $route)).to.be(false);
});
});
describe('but the search params are different', function () {
it('returns false', function () {
next.search = {};
prev.search = { q: 'search term' };
expect(kbnUrl._shouldAutoReload(next, prev)).to.be(false);
expect(kbnUrl._shouldAutoReload(next, prev, $route)).to.be(false);
});
});
});
@ -481,14 +472,14 @@ describe('kbnUrl', function () {
describe('and the path is the same', function () {
describe('and the search params are the same', function () {
it('returns true', function () {
expect(kbnUrl._shouldAutoReload(next, prev)).to.be(true);
expect(kbnUrl._shouldAutoReload(next, prev, $route)).to.be(true);
});
});
describe('but the search params are different', function () {
it('returns true', function () {
next.search = {};
prev.search = { q: 'search term' };
expect(kbnUrl._shouldAutoReload(next, prev)).to.be(true);
expect(kbnUrl._shouldAutoReload(next, prev, $route)).to.be(true);
});
});
});
@ -500,14 +491,14 @@ describe('kbnUrl', function () {
describe('and the search params are the same', function () {
it('returns false', function () {
expect(kbnUrl._shouldAutoReload(next, prev)).to.be(false);
expect(kbnUrl._shouldAutoReload(next, prev, $route)).to.be(false);
});
});
describe('but the search params are different', function () {
it('returns false', function () {
next.search = {};
prev.search = { q: 'search term' };
expect(kbnUrl._shouldAutoReload(next, prev)).to.be(false);
expect(kbnUrl._shouldAutoReload(next, prev, $route)).to.be(false);
});
});
});

View file

@ -3,12 +3,12 @@ import 'ui/filters/uriescape';
import 'ui/filters/rison';
import uiModules from 'ui/modules';
import rison from 'rison-node';
import AppStateProvider from 'ui/state_management/app_state';
uiModules.get('kibana/url')
.service('kbnUrl', function (Private) { return Private(KbnUrlProvider); });
function KbnUrlProvider($route, $location, $rootScope, globalState, $parse, getAppState) {
function KbnUrlProvider($injector, $location, $rootScope, $parse, Private) {
let self = this;
/**
@ -162,21 +162,25 @@ function KbnUrlProvider($route, $location, $rootScope, globalState, $parse, getA
search: $location.search()
};
if (self._shouldAutoReload(next, prev)) {
let appState = getAppState();
if (appState) appState.destroy();
if ($injector.has('$route')) {
const $route = $injector.get('$route');
reloading = $rootScope.$on('$locationChangeSuccess', function () {
// call the "unlisten" function returned by $on
reloading();
reloading = false;
if (self._shouldAutoReload(next, prev, $route)) {
const appState = Private(AppStateProvider).getAppState();
if (appState) appState.destroy();
$route.reload();
});
reloading = $rootScope.$on('$locationChangeSuccess', function () {
// call the "unlisten" function returned by $on
reloading();
reloading = false;
$route.reload();
});
}
}
};
self._shouldAutoReload = function (next, prev) {
self._shouldAutoReload = function (next, prev, $route) {
if (reloading) return false;
let route = $route.current && $route.current.$$route;

View file

@ -9,18 +9,22 @@ describe('slugifyId()', function () {
['test?test', 'test-questionmark-test'],
['test=test', 'test-equal-test'],
['test&test', 'test-ampersand-test'],
['test%test', 'test-percent-test'],
['test / test', 'test-slash-test'],
['test ? test', 'test-questionmark-test'],
['test = test', 'test-equal-test'],
['test & test', 'test-ampersand-test'],
['test % test', 'test-percent-test'],
['test / ^test', 'test-slash-^test'],
['test ? test', 'test-questionmark-test'],
['test = test', 'test-equal-test'],
['test & test', 'test-ampersand-test'],
['test % test', 'test-percent-test'],
['test/test/test', 'test-slash-test-slash-test'],
['test?test?test', 'test-questionmark-test-questionmark-test'],
['test&test&test', 'test-ampersand-test-ampersand-test'],
['test=test=test', 'test-equal-test-equal-test']
['test=test=test', 'test-equal-test-equal-test'],
['test%test%test', 'test-percent-test-percent-test']
];
_.each(fixtures, function (fixture) {

View file

@ -6,7 +6,8 @@ export default function (id) {
'/' : '-slash-',
'\\?' : '-questionmark-',
'\\&' : '-ampersand-',
'=' : '-equal-'
'=' : '-equal-',
'%' : '-percent-'
};
_.each(trans, function (val, key) {
let regex = new RegExp(key, 'g');

View file

@ -1,5 +1,4 @@
import d3 from 'd3';
import angular from 'angular';
import expect from 'expect.js';
import ngMock from 'ng_mock';
import VislibVisProvider from 'ui/vislib/vis';

View file

@ -95,10 +95,9 @@ export default function YAxisFactory(Private) {
* Return the domain for log scale, i.e. the extent of the log scale.
* Log scales must begin at 1 since the log(0) = -Infinity
*
* @param scale
* @param yMin
* @param yMax
* @returns {*[]}
* @param {Number} min
* @param {Number} max
* @returns {Array}
*/
YAxis.prototype._logDomain = function (min, max) {
if (min < 0 || max < 0) return this._throwLogScaleValuesError();

View file

@ -93,19 +93,13 @@ export default function VisFactory(Private) {
try {
this.handler[method]();
} catch (error) {
// If involving height and width of the container, log error to screen.
// Because we have to wait for the DOM element to initialize, we do not
// want to throw an error when the DOM `el` is zero
if (error instanceof errors.ContainerTooSmall ||
error instanceof errors.InvalidWiggleSelection ||
error instanceof errors.InvalidLogScaleValues ||
error instanceof errors.PieContainsAllZeros ||
error instanceof errors.NotEnoughData ||
error instanceof errors.NoResults) {
this.handler.error(error.message);
if (error instanceof errors.KbnError) {
error.displayToScreen(this.handler);
} else {
throw error;
}
}
};

View file

@ -2,6 +2,8 @@ import d3 from 'd3';
import _ from 'lodash';
import VislibVisualizationsChartProvider from 'ui/vislib/visualizations/_chart';
import VislibComponentsTooltipProvider from 'ui/vislib/components/tooltip';
import errors from 'ui/errors';
export default function PointSeriesChartProvider(Private) {
let Chart = Private(VislibVisualizationsChartProvider);
@ -68,13 +70,18 @@ export default function PointSeriesChartProvider(Private) {
}));
};
PointSeriesChart.prototype._invalidLogScaleValues = function (data) {
return data.series && data.series.some(function (d) {
return d.values && d.values.some(function (e) {
return e.y < 1;
});
});
PointSeriesChart.prototype.validateDataCompliesWithScalingMethod = function (data) {
const invalidLogScale = data.series && data.series.some(valuesSmallerThanOne);
if (this._attr.scale === 'log' && invalidLogScale) {
throw new errors.InvalidLogScaleValues();
}
};
function valuesSmallerThanOne(d) {
return d.values && d.values.some(e => e.y < 1);
}
/**
* Creates rects to show buckets outside of the ordered.min and max, returns rects

View file

@ -3,13 +3,10 @@ import _ from 'lodash';
import $ from 'jquery';
import moment from 'moment';
import errors from 'ui/errors';
import VislibLibDataProvider from 'ui/vislib/lib/data';
import VislibVisualizationsPointSeriesChartProvider from 'ui/vislib/visualizations/_point_series_chart';
import VislibVisualizationsTimeMarkerProvider from 'ui/vislib/visualizations/time_marker';
export default function ColumnChartFactory(Private) {
let DataClass = Private(VislibLibDataProvider);
let PointSeriesChart = Private(VislibVisualizationsPointSeriesChartProvider);
let TimeMarker = Private(VislibVisualizationsTimeMarkerProvider);
@ -118,7 +115,6 @@ export default function ColumnChartFactory(Private) {
let yScale = this.handler.yAxis.yScale;
let height = yScale.range()[0];
let yMin = this.handler.yAxis.yScale.domain()[0];
let self = this;
let barWidth;
if (data.ordered && data.ordered.date) {
@ -178,7 +174,6 @@ export default function ColumnChartFactory(Private) {
ColumnChart.prototype.addGroupedBars = function (bars) {
let xScale = this.handler.xAxis.xScale;
let yScale = this.handler.yAxis.yScale;
let yMin = this.handler.yAxis.yMin;
let data = this.chartData;
let n = data.series.length;
let height = yScale.range()[0];
@ -263,7 +258,6 @@ export default function ColumnChartFactory(Private) {
let margin = this._attr.margin;
let elWidth = this._attr.width = $elem.width();
let elHeight = this._attr.height = $elem.height();
let yMin = this.handler.yAxis.yMin;
let yScale = this.handler.yAxis.yScale;
let xScale = this.handler.xAxis.xScale;
let minWidth = 20;
@ -284,13 +278,22 @@ export default function ColumnChartFactory(Private) {
width = elWidth;
height = elHeight - margin.top - margin.bottom;
if (width < minWidth || height < minHeight) {
throw new errors.ContainerTooSmall();
}
self.validateDataCompliesWithScalingMethod(data);
if (addTimeMarker) {
timeMarker = new TimeMarker(times, xScale, height);
}
if (width < minWidth || height < minHeight) {
throw new errors.ContainerTooSmall();
if (
data.series.length > 1 &&
(self._attr.scale === 'log' || self._attr.scale === 'square root') &&
(self._attr.mode === 'stacked' || self._attr.mode === 'percentage')
) {
throw new errors.StackedBarChartConfig(`Cannot display ${self._attr.mode} bar charts for multiple data series \
with a ${self._attr.scale} scaling method. Try 'linear' scaling instead.`);
}
div = d3.select(this);

View file

@ -303,18 +303,16 @@ export default function LineChartFactory(Private) {
width = elWidth - margin.left - margin.right;
height = elHeight - margin.top - margin.bottom;
if (width < minWidth || height < minHeight) {
throw new errors.ContainerTooSmall();
}
self.validateDataCompliesWithScalingMethod(data);
if (addTimeMarker) {
timeMarker = new TimeMarker(times, xScale, height);
}
if (self._attr.scale === 'log' && self._invalidLogScaleValues(data)) {
throw new errors.InvalidLogScaleValues();
}
if (width < minWidth || height < minHeight) {
throw new errors.ContainerTooSmall();
}
div = d3.select(el);

View file

@ -9,13 +9,16 @@
ng-mouseenter="highlight($event)"
ng-mouseleave="unhighlight($event)"
data-label="{{legendData.label}}"
class="legend-value color">
class="legend-value color"
>
<div class="legend-value-container">
<div
ng-click="showDetails = !showDetails"
ng-class="showDetails ? 'legend-value-full' : 'legend-value-truncate'"
class="legend-value-title">
class="legend-value-title"
tooltip="{{legendData.label}}"
>
<i class="fa fa-circle" ng-style="{color: getColor(legendData.label)}"></i> {{legendData.label}}
</div>
@ -29,7 +32,8 @@
<i ng-repeat="choice in colors"
ng-click="setColor(legendData.label, choice)"
ng-class="choice == getColor(legendData.label) ? 'fa-circle-o' : 'fa-circle'"
ng-style="{color: choice}" class="fa dot">
ng-style="{color: choice}" class="fa dot"
>
</i>
</div>

View file

@ -102,10 +102,33 @@ describe('ui settings', function () {
describe('#getDefaults()', function () {
it('is promised the default values', async function () {
const { server, uiSettings, configGet } = instantiate();
const {server, uiSettings, configGet} = instantiate();
const defaults = await uiSettings.getDefaults();
expect(isEqual(defaults, defaultsProvider())).to.equal(true);
});
describe('defaults for formatters', async function () {
const defaults = defaultsProvider();
const mapping = JSON.parse(defaults['format:defaultTypeMap'].value);
const expected = {
ip: {id: 'ip', params: {}},
date: {id: 'date', params: {}},
number: {id: 'number', params: {}},
boolean: {id: 'boolean', params: {}},
_source: {id: '_source', params: {}},
_default_: {id: 'string', params: {}}
};
Object.keys(mapping).forEach(function (dataType) {
it(`should configure ${dataType}`, function () {
expect(expected.hasOwnProperty(dataType)).to.equal(true);
expect(mapping[dataType].id).to.equal(expected[dataType].id);
expect(JSON.stringify(mapping[dataType].params)).to.equal(JSON.stringify(expected[dataType].params));
});
});
});
});
describe('#getUserProvided()', function () {

View file

@ -160,6 +160,7 @@ export default function defaultSettingsProvider() {
"ip": { "id": "ip", "params": {} },
"date": { "id": "date", "params": {} },
"number": { "id": "number", "params": {} },
"boolean": { "id": "boolean", "params": {} },
"_source": { "id": "_source", "params": {} },
"_default_": { "id": "string", "params": {} }
}`,

View file

@ -62,6 +62,18 @@ bdd.describe('visualize app', function describeIndexTests() {
bdd.describe('area charts', function indexPatternCreation() {
var vizName1 = 'Visualization AreaChart';
bdd.it('should save and load with special characters', function pageHeader() {
const vizNamewithSpecialChars = vizName1 + '/?&=%';
return PageObjects.visualize.saveVisualization(vizNamewithSpecialChars)
.then(function (message) {
PageObjects.common.debug(`Saved viz message = ${message}`);
expect(message).to.be(`Visualization Editor: Saved Visualization "${vizNamewithSpecialChars}"`);
})
.then(function testVisualizeWaitForToastMessageGone() {
return PageObjects.visualize.waitForToastMessageGone();
});
});
bdd.it('should save and load', function pageHeader() {
return PageObjects.visualize.saveVisualization(vizName1)
.then(function (message) {

View file

@ -175,7 +175,7 @@ define(function (require) {
method: 'GET'
})
.then(function (body) {
expect(body.pipelines[0].id).to.be('kibana-logstash-*');
expect(body).to.have.property('kibana-logstash-*');
});
});
});