diff --git a/docs/visualize.asciidoc b/docs/visualize.asciidoc
index 1aa8076d578ea..5412591685701 100644
--- a/docs/visualize.asciidoc
+++ b/docs/visualize.asciidoc
@@ -118,3 +118,5 @@ include::visualize/pie.asciidoc[]
include::visualize/tilemap.asciidoc[]
include::visualize/vertbar.asciidoc[]
+
+include::visualize/tagcloud.asciidoc[]
diff --git a/docs/visualize/tagcloud.asciidoc b/docs/visualize/tagcloud.asciidoc
new file mode 100644
index 0000000000000..b06e57a722e47
--- /dev/null
+++ b/docs/visualize/tagcloud.asciidoc
@@ -0,0 +1,44 @@
+[[tagcloud-chart]]
+== Cloud Tag Charts
+
+A tag cloud visualization is a visual representation of text data, typically used to visualize free form text.
+Tags are usually single words, and the importance of each tag is shown with font size or color.
+
+The font size for each word is determined by the _metrics_ aggregation. The following aggregations are available for
+this chart:
+
+include::y-axis-aggs.asciidoc[]
+
+
+The _buckets_ aggregations determine what information is being retrieved from your data set.
+
+Before you choose a buckets aggregation, select the *Split Tags* option.
+
+You can specify the following bucket aggregations for tag cloud visualization:
+
+*Terms*:: A {es-ref}search-aggregations-bucket-terms-aggregation.html[_terms_] aggregation enables you to specify the top
+or bottom _n_ elements of a given field to display, ordered by count or a custom metric.
+
+You can click the *Advanced* link to display more customization options for your metrics or bucket aggregation:
+
+*JSON Input*:: A text field where you can add specific JSON-formatted properties to merge with the aggregation
+definition, as in the following example:
+
+[source,shell]
+{ "script" : "doc['grade'].value * 1.2" }
+
+NOTE: In Elasticsearch releases 1.4.3 and later, this functionality requires you to enable
+{es-ref}modules-scripting.html[dynamic Groovy scripting].
+
+
+Select the *Options* tab to change the following aspects of the chart:
+
+*Text Scale*:: You can select *linear*, *log*, or *square root* scales for the text scale. You can use a log
+scale to display data that varies exponentially or a square root scale to
+regularize the display of data sets with variabilities that are themselves highly variable.
+*Orientation*:: You can select how to orientate your text in the tag cloud. You can choose one of the following options:
+Single, right angles and multiple.
+*Font Size*:: Allows you to set minimum and maximum font size to use for this visualization.
+
+
+include::visualization-raw-data.asciidoc[]
\ No newline at end of file
diff --git a/package.json b/package.json
index 1380678cc5d74..b26563945552c 100644
--- a/package.json
+++ b/package.json
@@ -101,6 +101,7 @@
"commander": "2.8.1",
"css-loader": "0.17.0",
"d3": "3.5.6",
+ "d3-cloud": "1.2.1",
"dragula": "3.7.0",
"elasticsearch": "12.0.0-rc5",
"elasticsearch-browser": "12.0.0-rc5",
@@ -138,6 +139,7 @@
"mkdirp": "0.5.1",
"moment": "2.13.0",
"moment-timezone": "0.5.4",
+ "no-ui-slider": "1.2.0",
"node-fetch": "1.3.2",
"node-uuid": "1.4.7",
"pegjs": "0.9.0",
diff --git a/src/core_plugins/elasticsearch/lib/__tests__/check_es_version.js b/src/core_plugins/elasticsearch/lib/__tests__/check_es_version.js
index 74d43816d8384..cc36d350d6eb0 100644
--- a/src/core_plugins/elasticsearch/lib/__tests__/check_es_version.js
+++ b/src/core_plugins/elasticsearch/lib/__tests__/check_es_version.js
@@ -56,6 +56,12 @@ describe('plugins/elasticsearch', () => {
client.nodes.info = sinon.stub().returns(Promise.resolve({ nodes: nodes }));
}
+ function setNodeWithoutHTTP(version) {
+ const nodes = { 'node-without-http': { version, ip: 'ip' } };
+ const client = server.plugins.elasticsearch.client;
+ client.nodes.info = sinon.stub().returns(Promise.resolve({ nodes: nodes }));
+ }
+
it('returns true with single a node that matches', async () => {
setNodes('5.1.0');
const result = await checkEsVersion(server, KIBANA_VERSION);
@@ -99,6 +105,24 @@ describe('plugins/elasticsearch', () => {
expect(server.log.getCall(1).args[0]).to.contain('warning');
});
+ it('warns if a node is off by a patch version and without http publish address', async () => {
+ setNodeWithoutHTTP('5.1.1');
+ await checkEsVersion(server, KIBANA_VERSION);
+ sinon.assert.callCount(server.log, 2);
+ expect(server.log.getCall(0).args[0]).to.contain('debug');
+ expect(server.log.getCall(1).args[0]).to.contain('warning');
+ });
+
+ it('errors if a node incompatible and without http publish address', async () => {
+ setNodeWithoutHTTP('6.1.1');
+ try {
+ await checkEsVersion(server, KIBANA_VERSION);
+ } catch (e) {
+ expect(e.message).to.contain('incompatible nodes');
+ expect(e).to.be.a(Error);
+ }
+ });
+
it('only warns once per node list', async () => {
setNodes('5.1.1');
diff --git a/src/core_plugins/elasticsearch/lib/__tests__/get_basic_auth_realm.js b/src/core_plugins/elasticsearch/lib/__tests__/get_basic_auth_realm.js
deleted file mode 100644
index 7dc84e75730c8..0000000000000
--- a/src/core_plugins/elasticsearch/lib/__tests__/get_basic_auth_realm.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import getBasicAuthRealm from '../get_basic_auth_realm';
-import expect from 'expect.js';
-const exception = '[security_exception] missing authentication token for REST request [/logstash-*/_search],' +
- ' with: {"header":{"WWW-Authenticate":"Basic realm=\\"shield\\""}}';
-
-
-describe('plugins/elasticsearch', function () {
- describe('lib/get_basic_auth_realm', function () {
-
- it('should return null if passed something other than a string', function () {
- expect(getBasicAuthRealm({})).to.be(null);
- expect(getBasicAuthRealm(500)).to.be(null);
- expect(getBasicAuthRealm([exception])).to.be(null);
- });
-
- // TODO: This should be updated to match header strings when the client supports that
- it('should return the realm when passed an elasticsearch security exception', function () {
- expect(getBasicAuthRealm(exception)).to.be('shield');
- });
-
- it('should return null when no basic realm information is found', function () {
- expect(getBasicAuthRealm('Basically nothing="the universe"')).to.be(null);
- });
-
- });
-});
-
diff --git a/src/core_plugins/elasticsearch/lib/call_with_request.js b/src/core_plugins/elasticsearch/lib/call_with_request.js
index 870b856de5fce..ed53ee36ca372 100644
--- a/src/core_plugins/elasticsearch/lib/call_with_request.js
+++ b/src/core_plugins/elasticsearch/lib/call_with_request.js
@@ -1,14 +1,14 @@
import _ from 'lodash';
import Promise from 'bluebird';
import Boom from 'boom';
-import getBasicAuthRealm from './get_basic_auth_realm';
import toPath from 'lodash/internal/toPath';
import filterHeaders from './filter_headers';
module.exports = (server, client) => {
- return (req, endpoint, params = {}) => {
+ return (req, endpoint, clientParams = {}, options = {}) => {
+ const wrap401Errors = options.wrap401Errors !== false;
const filteredHeaders = filterHeaders(req.headers, server.config().get('elasticsearch.requestHeadersWhitelist'));
- _.set(params, 'headers', filteredHeaders);
+ _.set(clientParams, 'headers', filteredHeaders);
const path = toPath(endpoint);
const api = _.get(client, path);
let apiContext = _.get(client, path.slice(0, -1));
@@ -16,16 +16,16 @@ module.exports = (server, client) => {
apiContext = client;
}
if (!api) throw new Error(`callWithRequest called with an invalid endpoint: ${endpoint}`);
- return api.call(apiContext, params)
+ return api.call(apiContext, clientParams)
.catch((err) => {
- if (err.status === 401) {
- // TODO: The err.message is temporary until we have support for getting headers in the client.
- // Once we have that, we should be able to pass the contents of the WWW-Authenticate head to getRealm
- const realm = getBasicAuthRealm(err.message) || 'Authorization Required';
- const options = { realm: realm };
- return Promise.reject(Boom.unauthorized('Unauthorized', 'Basic', options));
+ if (!wrap401Errors || err.statusCode !== 401) {
+ return Promise.reject(err);
}
- return Promise.reject(err);
+
+ const boomError = Boom.wrap(err, err.statusCode);
+ const wwwAuthHeader = _.get(err, 'body.error.header[WWW-Authenticate]');
+ boomError.output.headers['WWW-Authenticate'] = wwwAuthHeader || 'Basic realm="Authorization Required"';
+ throw boomError;
});
};
};
diff --git a/src/core_plugins/elasticsearch/lib/check_es_version.js b/src/core_plugins/elasticsearch/lib/check_es_version.js
index b9eddd0e0f249..a6cc89978ac9b 100644
--- a/src/core_plugins/elasticsearch/lib/check_es_version.js
+++ b/src/core_plugins/elasticsearch/lib/check_es_version.js
@@ -44,7 +44,8 @@ module.exports = function checkEsVersion(server, kibanaVersion) {
function getHumanizedNodeNames(nodes) {
return nodes.map(node => {
- return 'v' + node.version + ' @ ' + node.http.publish_address + ' (' + node.ip + ')';
+ const publishAddress = _.get(node, 'http.publish_address') ? (_.get(node, 'http.publish_address') + ' ') : '';
+ return 'v' + node.version + ' @ ' + publishAddress + '(' + node.ip + ')';
});
}
@@ -52,7 +53,7 @@ module.exports = function checkEsVersion(server, kibanaVersion) {
const simplifiedNodes = warningNodes.map(node => ({
version: node.version,
http: {
- publish_address: node.http.publish_address,
+ publish_address: _.get(node, 'http.publish_address')
},
ip: node.ip,
}));
@@ -78,7 +79,7 @@ module.exports = function checkEsVersion(server, kibanaVersion) {
throw new Error(
`This version of Kibana requires Elasticsearch v` +
`${kibanaVersion} on all nodes. I found ` +
- `the following incompatible nodes in your cluster: ${incompatibleNodeNames.join(',')}`
+ `the following incompatible nodes in your cluster: ${incompatibleNodeNames.join(', ')}`
);
}
diff --git a/src/core_plugins/elasticsearch/lib/get_basic_auth_realm.js b/src/core_plugins/elasticsearch/lib/get_basic_auth_realm.js
deleted file mode 100644
index 9a9c183def983..0000000000000
--- a/src/core_plugins/elasticsearch/lib/get_basic_auth_realm.js
+++ /dev/null
@@ -1,7 +0,0 @@
-export default function getBasicAuthRealm(message) {
- if (!message || typeof message !== 'string') return null;
-
- const parts = message.match(/Basic\ realm=\\"(.*)\\"/);
- if (parts && parts.length === 2) return parts[1];
- else return null;
-};
\ No newline at end of file
diff --git a/src/core_plugins/kbn_vislib_vis_types/public/area.js b/src/core_plugins/kbn_vislib_vis_types/public/area.js
index 9e8e80d5f89cd..cf33aaef09a04 100644
--- a/src/core_plugins/kbn_vislib_vis_types/public/area.js
+++ b/src/core_plugins/kbn_vislib_vis_types/public/area.js
@@ -47,6 +47,7 @@ export default function HistogramVisType(Private) {
modes: ['stacked', 'overlap', 'percentage', 'wiggle', 'silhouette'],
editor: areaTemplate
},
+ implementsRenderComplete: true,
schemas: new Schemas([
{
group: 'metrics',
diff --git a/src/core_plugins/kbn_vislib_vis_types/public/histogram.js b/src/core_plugins/kbn_vislib_vis_types/public/histogram.js
index 1b2d803ac644b..3b3db63fddcdc 100644
--- a/src/core_plugins/kbn_vislib_vis_types/public/histogram.js
+++ b/src/core_plugins/kbn_vislib_vis_types/public/histogram.js
@@ -43,6 +43,7 @@ export default function HistogramVisType(Private) {
modes: ['stacked', 'percentage', 'grouped'],
editor: histogramTemplate
},
+ implementsRenderComplete: true,
schemas: new Schemas([
{
group: 'metrics',
diff --git a/src/core_plugins/kbn_vislib_vis_types/public/line.js b/src/core_plugins/kbn_vislib_vis_types/public/line.js
index a8b4eedff6535..c061d6183237d 100644
--- a/src/core_plugins/kbn_vislib_vis_types/public/line.js
+++ b/src/core_plugins/kbn_vislib_vis_types/public/line.js
@@ -46,6 +46,7 @@ export default function HistogramVisType(Private) {
scales: ['linear', 'log', 'square root'],
editor: lineTemplate
},
+ implementsRenderComplete: true,
schemas: new Schemas([
{
group: 'metrics',
diff --git a/src/core_plugins/kbn_vislib_vis_types/public/pie.js b/src/core_plugins/kbn_vislib_vis_types/public/pie.js
index 64b4b4688156f..9a66fd9924ccb 100644
--- a/src/core_plugins/kbn_vislib_vis_types/public/pie.js
+++ b/src/core_plugins/kbn_vislib_vis_types/public/pie.js
@@ -37,6 +37,7 @@ export default function HistogramVisType(Private) {
},
responseConverter: false,
hierarchicalData: true,
+ implementsRenderComplete: true,
schemas: new Schemas([
{
group: 'metrics',
diff --git a/src/core_plugins/kbn_vislib_vis_types/public/tile_map.js b/src/core_plugins/kbn_vislib_vis_types/public/tile_map.js
index 875c079a2558e..1a79dfd35ac19 100644
--- a/src/core_plugins/kbn_vislib_vis_types/public/tile_map.js
+++ b/src/core_plugins/kbn_vislib_vis_types/public/tile_map.js
@@ -81,6 +81,7 @@ export default function TileMapVisType(Private, getAppState, courier, config) {
}
},
responseConverter: geoJsonConverter,
+ implementsRenderComplete: true,
schemas: new Schemas([
{
group: 'metrics',
diff --git a/src/core_plugins/kibana/public/dashboard/components/panel/panel.html b/src/core_plugins/kibana/public/dashboard/components/panel/panel.html
index 6665034471980..874a62b75522a 100644
--- a/src/core_plugins/kibana/public/dashboard/components/panel/panel.html
+++ b/src/core_plugins/kibana/public/dashboard/components/panel/panel.html
@@ -28,13 +28,16 @@
search-source="savedObj.searchSource"
show-spy-panel="chrome.getVisible()"
ui-state="uiState"
+ render-counter
class="panel-content">
-