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"> - diff --git a/src/core_plugins/kibana/public/discover/index.html b/src/core_plugins/kibana/public/discover/index.html index 1a60d56b6b641..0f2faa466bc67 100644 --- a/src/core_plugins/kibana/public/discover/index.html +++ b/src/core_plugins/kibana/public/discover/index.html @@ -125,7 +125,8 @@

Searching

sorting="state.sort" columns="state.columns" infinite-scroll="true" - filter="filterQuery"> + filter="filterQuery" + render-counter>