diff --git a/package.json b/package.json index 70417f63d7446..8291ebbba9992 100644 --- a/package.json +++ b/package.json @@ -151,6 +151,7 @@ "auto-release-sinon": "1.0.3", "babel-eslint": "4.1.8", "chokidar": "1.4.3", + "elasticdump": "2.1.1", "eslint": "1.10.3", "eslint-plugin-mocha": "1.1.0", "expect.js": "0.3.1", diff --git a/test/fixtures/dump_data/dashboard.data.json b/test/fixtures/dump_data/dashboard.data.json new file mode 100644 index 0000000000000..09239ffa6b8f4 --- /dev/null +++ b/test/fixtures/dump_data/dashboard.data.json @@ -0,0 +1,8 @@ +{"_index":".kibana","_type":"index-pattern","_id":"logstash-*","_score":1,"_source":{"title":"logstash-*","timeFieldName":"@timestamp","fields":"[{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false}]"}} +{"_index":".kibana","_type":"visualization","_id":"Visualization-TileMap","_score":1,"_source":{"title":"Visualization TileMap","visState":"{\"title\":\"New Visualization\",\"type\":\"tile_map\",\"params\":{\"mapType\":\"Scaled Circle Markers\",\"isDesaturated\":true,\"addTooltip\":true,\"heatMaxZoom\":16,\"heatMinOpacity\":0.1,\"heatRadius\":25,\"heatBlur\":15,\"heatNormalizeData\":true,\"wms\":{\"enabled\":false,\"url\":\"https://basemap.nationalmap.gov/arcgis/services/USGSTopo/MapServer/WMSServer\",\"options\":{\"version\":\"1.3.0\",\"layers\":\"0\",\"format\":\"image/png\",\"transparent\":true,\"attribution\":\"Maps provided by USGS\",\"styles\":\"\"}}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"geohash_grid\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.coordinates\",\"autoPrecision\":true,\"precision\":2}}],\"listeners\":{}}","uiStateJSON":"{}","description":"","version":1,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"}}} +{"_index":".kibana","_type":"visualization","_id":"Visualization漢字-LineChart","_score":1,"_source":{"title":"Visualization漢字 LineChart","visState":"{\"title\":\"New Visualization\",\"type\":\"line\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"showCircles\":true,\"smoothLines\":false,\"interpolate\":\"linear\",\"scale\":\"linear\",\"drawLinesBetweenPoints\":true,\"radiusRatio\":9,\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"extension.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"row\":false}}],\"listeners\":{}}","uiStateJSON":"{}","description":"","version":1,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"}}} +{"_index":".kibana","_type":"visualization","_id":"Visualization漢字-AreaChart","_score":1,"_source":{"title":"Visualization漢字 AreaChart","visState":"{\"title\":\"New Visualization\",\"type\":\"area\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"smoothLines\":false,\"scale\":\"linear\",\"interpolate\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}}],\"listeners\":{}}","uiStateJSON":"{}","description":"","version":1,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"}}} +{"_index":".kibana","_type":"visualization","_id":"Visualization☺-VerticalBarChart","_score":1,"_source":{"title":"Visualization☺ VerticalBarChart","visState":"{\"title\":\"New Visualization\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}}],\"listeners\":{}}","uiStateJSON":"{}","description":"","version":1,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"}}} +{"_index":".kibana","_type":"visualization","_id":"Visualization☺漢字-DataTable","_score":1,"_source":{"title":"Visualization☺漢字 DataTable","visState":"{\"title\":\"New Visualization\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"histogram\",\"schema\":\"bucket\",\"params\":{\"field\":\"bytes\",\"interval\":2000,\"extended_bounds\":{}}}],\"listeners\":{}}","uiStateJSON":"{}","description":"","version":1,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"}}} +{"_index":".kibana","_type":"visualization","_id":"Visualization-PieChart","_score":1,"_source":{"title":"Visualization PieChart","visState":"{\"title\":\"New Visualization\",\"type\":\"pie\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"isDonut\":false},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"memory\",\"interval\":40000,\"extended_bounds\":{}}}],\"listeners\":{}}","uiStateJSON":"{}","description":"","version":1,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"}}} +{"_index":".kibana","_type":"visualization","_id":"Visualization-MetricChart","_score":1,"_source":{"title":"Visualization MetricChart","visState":"{\"title\":\"New Visualization\",\"type\":\"metric\",\"params\":{\"handleNoResults\":true,\"fontSize\":60},\"aggs\":[{\"id\":\"1\",\"type\":\"percentile_ranks\",\"schema\":\"metric\",\"params\":{\"field\":\"memory\",\"values\":[99]}}],\"listeners\":{}}","uiStateJSON":"{}","description":"","version":1,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"}}} diff --git a/test/fixtures/dump_data/dashboard.mapping.json b/test/fixtures/dump_data/dashboard.mapping.json new file mode 100644 index 0000000000000..ff55e2b8221c4 --- /dev/null +++ b/test/fixtures/dump_data/dashboard.mapping.json @@ -0,0 +1 @@ +{".kibana":{"mappings":{"config":{"properties":{"buildNum":{"type":"keyword"}}},"index-pattern":{"properties":{"fields":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"timeFieldName":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"title":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}}}},"search":{"properties":{"columns":{"type":"text"},"description":{"type":"text"},"hits":{"type":"integer"},"kibanaSavedObjectMeta":{"properties":{"searchSourceJSON":{"type":"text"}}},"sort":{"type":"text"},"title":{"type":"text"},"version":{"type":"integer"}}},"visualization":{"properties":{"description":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"kibanaSavedObjectMeta":{"properties":{"searchSourceJSON":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}}}},"title":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"uiStateJSON":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"version":{"type":"integer"},"visState":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}}}},"server":{"properties":{"uuid":{"type":"keyword"}}},"dashboard":{"properties":{"description":{"type":"text"},"hits":{"type":"integer"},"kibanaSavedObjectMeta":{"properties":{"searchSourceJSON":{"type":"text"}}},"optionsJSON":{"type":"text"},"panelsJSON":{"type":"text"},"timeFrom":{"type":"text"},"timeRestore":{"type":"boolean"},"timeTo":{"type":"text"},"title":{"type":"text"},"uiStateJSON":{"type":"text"},"version":{"type":"integer"}}}}}} diff --git a/test/functional/apps/console/_console.js b/test/functional/apps/console/_console.js index c214e3d3971d9..d1cc4c4ae96da 100644 --- a/test/functional/apps/console/_console.js +++ b/test/functional/apps/console/_console.js @@ -2,8 +2,6 @@ import { bdd, scenarioManager, common, - // settingsPage, - // headerPage, consolePage } from '../../../support'; diff --git a/test/functional/apps/console/index.js b/test/functional/apps/console/index.js index 057a57e52cfae..7e48a17b7baf5 100644 --- a/test/functional/apps/console/index.js +++ b/test/functional/apps/console/index.js @@ -8,10 +8,6 @@ import { bdd, remote, scenarioManager, defaultTimeout } from '../../../support'; return remote.setWindowSize(1200,800); }); - bdd.after(function unloadMakelogs() { - return scenarioManager.unload('logstashFunctional'); - }); - require('./_console'); }); }()); diff --git a/test/functional/apps/dashboard/_dashboard.js b/test/functional/apps/dashboard/_dashboard.js new file mode 100644 index 0000000000000..3ccecc5ebf686 --- /dev/null +++ b/test/functional/apps/dashboard/_dashboard.js @@ -0,0 +1,140 @@ +import { + bdd, + common, + dashboardPage, + headerPage, + scenarioManager, + esClient, + elasticDump +} from '../../../support'; + +(function () { + var expect = require('expect.js'); + + (function () { + bdd.describe('dashboard tab', function describeIndexTests() { + + bdd.before(function () { + + var fromTime = '2015-09-19 06:31:44.000'; + var toTime = '2015-09-23 18:31:44.000'; + + common.debug('Starting dashboard before method'); + common.debug('navigateToApp dashboard'); + return esClient.delete('.kibana') + .then(function () { + return common.try(function () { + return esClient.updateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'}); + }); + }) + // and load a set of makelogs data + .then(function loadkibana4() { + common.debug('load kibana index with visualizations'); + return elasticDump.elasticLoad('dashboard','.kibana'); + }) + .then(function () { + return scenarioManager.loadIfEmpty('logstashFunctional'); + }) + .then(function () { + return common.navigateToApp('dashboard'); + }) + .catch(common.handleError(this)); + }); + + + bdd.describe('add visualizations to dashboard', function dashboardTest() { + var visualizations = ['Visualization漢字 AreaChart', + 'Visualization☺漢字 DataTable', + 'Visualization漢字 LineChart', + 'Visualization PieChart', + 'Visualization TileMap', + 'Visualization☺ VerticalBarChart', + 'Visualization MetricChart' + ]; + + + bdd.it('should be able to add visualizations to dashboard', function addVisualizations() { + + function addVisualizations(arr) { + return arr.reduce(function (promise, vizName) { + return promise + .then(function () { + return dashboardPage.addVisualization(vizName); + }); + }, Promise.resolve()); + } + + return addVisualizations(visualizations) + .then(function () { + common.debug('done adding visualizations'); + }); + + }); + + bdd.it('set the timepicker time to that which contains our test data', function setTimepicker() { + var fromTime = '2015-09-19 06:31:44.000'; + var toTime = '2015-09-23 18:31:44.000'; + var testSubName = 'Dashboard Test 1'; + + // .then(function () { + common.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"'); + return headerPage.setAbsoluteRange(fromTime, toTime) + .then(function sleep() { + return common.sleep(4000); + }) + .then(function takeScreenshot() { + common.debug('Take screenshot'); + common.saveScreenshot('./screenshot-' + testSubName + '.png'); + }) + .catch(common.handleError(this)); + }); + + bdd.it('should save and load dashboard', function saveAndLoadDashboard() { + var testSubName = 'Dashboard Test 1'; +// save time on the dashboard? + return dashboardPage.saveDashboard(testSubName) + // click New Dashboard just to clear the one we just created + .then(function () { + return dashboardPage.clickNewDashboard(); + }) + .then(function () { + return dashboardPage.loadSavedDashboard(testSubName); + }) + .catch(common.handleError(this)); + }); + + bdd.it('should have all the expected visualizations', function checkVisualizations() { + return common.tryForTime(10000, function () { + return dashboardPage.getPanelTitles() + .then(function (panelTitles) { + common.log('visualization titles = ' + panelTitles); + expect(panelTitles).to.eql(visualizations); + }); + }) + .catch(common.handleError(this)); + }); + + bdd.it('should have all the expected initial sizes', function checkVisualizationSizes() { + var visObjects = [ { dataCol: '1', dataRow: '1', dataSizeX: '3', dataSizeY: '2', title: 'Visualization漢字 AreaChart' }, + { dataCol: '4', dataRow: '1', dataSizeX: '3', dataSizeY: '2', title: 'Visualization☺漢字 DataTable' }, + { dataCol: '7', dataRow: '1', dataSizeX: '3', dataSizeY: '2', title: 'Visualization漢字 LineChart' }, + { dataCol: '10', dataRow: '1', dataSizeX: '3', dataSizeY: '2', title: 'Visualization PieChart' }, + { dataCol: '1', dataRow: '3', dataSizeX: '3', dataSizeY: '2', title: 'Visualization TileMap' }, + { dataCol: '4', dataRow: '3', dataSizeX: '3', dataSizeY: '2', title: 'Visualization☺ VerticalBarChart' }, + { dataCol: '7', dataRow: '3', dataSizeX: '3', dataSizeY: '2', title: 'Visualization MetricChart' } + ]; + return common.tryForTime(10000, function () { + return dashboardPage.getPanelData() + .then(function (panelTitles) { + common.log('visualization titles = ' + panelTitles); + expect(panelTitles).to.eql(visObjects); + }); + }) + .catch(common.handleError(this)); + }); + + }); + + }); + }()); +}()); diff --git a/test/functional/apps/dashboard/index.js b/test/functional/apps/dashboard/index.js new file mode 100644 index 0000000000000..813e03d669820 --- /dev/null +++ b/test/functional/apps/dashboard/index.js @@ -0,0 +1,13 @@ +import { bdd, remote, scenarioManager, defaultTimeout } from '../../../support'; + +(function () { + bdd.describe('dashboard app', function () { + this.timeout = defaultTimeout; + + bdd.before(function () { + return remote.setWindowSize(1200,800); + }); + + require('./_dashboard'); + }); +}()); diff --git a/test/functional/index.js b/test/functional/index.js index 85caa6baac0bc..52202e316f58f 100644 --- a/test/functional/index.js +++ b/test/functional/index.js @@ -24,7 +24,8 @@ define(function (require) { 'intern/dojo/node!./status_page', 'intern/dojo/node!./apps/settings', 'intern/dojo/node!./apps/visualize', - 'intern/dojo/node!./apps/console' + 'intern/dojo/node!./apps/console', + 'intern/dojo/node!./apps/dashboard' ], function () {}); }); }); diff --git a/test/support/elastic_dump.js b/test/support/elastic_dump.js new file mode 100644 index 0000000000000..c08999d21b178 --- /dev/null +++ b/test/support/elastic_dump.js @@ -0,0 +1,109 @@ +import { common, config} from './'; + +export default (function () { + var util = require('util'); + var path = require('path'); + var url = require('url'); + var resolve = require('path').resolve; + var Elasticdump = require('elasticdump').elasticdump; + + function ElasticDump() { + } + + ElasticDump.prototype = { + + /* + ** This function is basically copied from + ** https://github.com/taskrabbit/elasticsearch-dump/blob/master/bin/elasticdump + ** and allows calling elasticdump for importing or exporting data from Elasticsearch + */ + elasticdumpModule: function elasticdumpModule(myinput, myoutput, index, mytype) { + + var options = { + limit: 100, + offset: 0, + debug: false, + type: mytype, + delete: false, + all: false, + maxSockets: null, + input: myinput, + 'input-index': null, + output: myoutput, + 'output-index': index, + inputTransport: null, + outputTransport: null, + searchBody: null, + sourceOnly: false, + jsonLines: false, + format: '', + 'ignore-errors': false, + scrollTime: '10m', + timeout: null, + skip: null, + toLog: null, + }; + var dumper = new Elasticdump(options.input, options.output, options); + + dumper.on('log', function (message) { common.debug(message); }); + dumper.on('error', function (error) { common.debug('error', 'Error Emitted => ' + (error.message || JSON.stringify(error))); }); + + var promise = new Promise(function (resolve, reject) { + dumper.dump(function (error, totalWrites) { + if (error) { + common.debug('THERE WAS AN ERROR :-('); + reject(Error(error)); + } else { + resolve ('elasticdumpModule success'); + } + }); + }); + return promise; + }, + + /* + ** Dumps data from Elasticsearch into json files. + ** Takes a simple filename as input like 'dashboard' (for dashboard tests). + ** Appends ''.mapping.json' and '.data.json' for the actual filenames. + ** Writes files to the Kibana root dir. + ** Fails if the files already exist, so consider appending a timestamp to filename. + */ + elasticDump: function elasticDump(index, file) { + var self = this; + common.debug('Dumping mapping from ' + url.format(config.servers.elasticsearch) + '/' + index + + ' to (' + file + '.mapping.json)'); + return this.elasticdumpModule(url.format(config.servers.elasticsearch), + file + '.mapping.json', index, 'mapping') + .then(function () { + common.debug('Dumping data from ' + url.format(config.servers.elasticsearch) + '/' + index + + ' to (' + file + '.data.json)'); + return self.elasticdumpModule(url.format(config.servers.elasticsearch), + file + '.data.json', index, 'data'); + }); + }, + + /* + ** Loads data from json files into Elasticsearch. + ** Takes a simple filename as input like 'dashboard' (for dashboard tests). + ** Appends ''.mapping.json' and '.data.json' for the actual filenames. + ** Path /test/fixtures/dump_data is hard-coded + */ + elasticLoad: function elasticLoad(file, index) { + // TODO: should we have a flag to delete the index first? + // or use scenarioManager.unload(index) ? <<- currently this + var self = this; + common.debug('Loading mapping (test/fixtures/dump_data/' + file + '.mapping.json) into ' + + url.format(config.servers.elasticsearch) + '/' + index); + return this.elasticdumpModule('test/fixtures/dump_data/' + file + '.mapping.json', + url.format(config.servers.elasticsearch), index, 'mapping') + .then(function () { + common.debug('Loading data (test/fixtures/dump_data/' + file + '.data.json) into ' + + url.format(config.servers.elasticsearch) + '/' + index); + return self.elasticdumpModule('test/fixtures/dump_data/' + file + '.data.json', + url.format(config.servers.elasticsearch), index, 'data'); + }); + }, + }; + + return ElasticDump; +}()); diff --git a/test/support/es_client.js b/test/support/es_client.js new file mode 100644 index 0000000000000..76f486b468437 --- /dev/null +++ b/test/support/es_client.js @@ -0,0 +1,96 @@ +import { common, remote} from './'; + +export default (function () { + + var elasticsearch = require('elasticsearch'); + var Promise = require('bluebird'); + + function EsClient(server) { + this.remote = remote; + if (!server) throw new Error('No server defined'); + + // NOTE: some large sets of test data can take several minutes to load + this.client = new elasticsearch.Client({ + host: server, + requestTimeout: 300000, + defer: function () { + return Promise.defer(); + } + }); + } + + EsClient.prototype = { + constructor: EsClient, + + + /** + * Delete an index + * @param {string} index + * @return {Promise} A promise that is resolved when elasticsearch has a response + */ + delete: function (index) { + + return this.client.indices.delete({ + index: index + }) + .catch(function (reason) { + // if the index never existed yet, or was already deleted it's OK + if (reason.message.indexOf('index_not_found_exception') < 0) { + common.debug('reason.message: ' + reason.message); + throw reason; + } + }); + }, + + + /** + * Add fields to the config doc (like setting timezone and defaultIndex) + * @return {Promise} A promise that is resolved when elasticsearch has a response + */ + updateConfigDoc: function (docMap) { + // first we need to get the config doc's id so we can use it in our _update call + var self = this; + var configId; + var docMapString = JSON.stringify(docMap); + + return this.client.search({ + index: '.kibana', + type: 'config' + }) + .then(function (response) { + if (response.errors) { + throw new Error( + 'get config failed\n' + + response.items + .map(i => i[Object.keys(i)[0]].error) + .filter(Boolean) + .map(err => ' ' + JSON.stringify(err)) + .join('\n') + ); + } else { + configId = response.hits.hits[0]._id; + common.debug('config._id =' + configId); + } + }) + // now that we have the id, we can update + // return scenarioManager.updateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'}); + .then(function (response) { + common.debug('updating config with ' + docMapString); + return self.client.update({ + index: '.kibana', + type: 'config', + id: configId, + body: { + 'doc': + docMap + } + }); + }) + .catch(function (err) { + throw err; + }); + } + }; + + return EsClient; +}()); diff --git a/test/support/index.js b/test/support/index.js index d044231ba3d47..476a40d1e8ee9 100644 --- a/test/support/index.js +++ b/test/support/index.js @@ -1,10 +1,13 @@ import url from 'url'; +import EsClient from './es_client'; +import ElasticDump from './elastic_dump'; import ScenarioManager from '../fixtures/scenario_manager'; import Common from './pages/common'; import DiscoverPage from './pages/discover_page'; import SettingsPage from './pages/settings_page'; import HeaderPage from './pages/header_page'; import VisualizePage from './pages/visualize_page'; +import DashboardPage from './pages/dashboard_page'; import ShieldPage from './pages/shield_page'; import ConsolePage from './pages/console_page'; @@ -17,6 +20,7 @@ exports.defaultTimeout = exports.config.defaultTimeout; exports.defaultTryTimeout = exports.config.defaultTryTimeout; exports.defaultFindTimeout = exports.config.defaultFindTimeout; exports.scenarioManager = new ScenarioManager(url.format(exports.config.servers.elasticsearch)); +exports.esClient = new EsClient(url.format(exports.config.servers.elasticsearch)); defineDelayedExport('remote', (suite) => suite.remote); defineDelayedExport('common', () => new Common()); @@ -24,8 +28,10 @@ defineDelayedExport('discoverPage', () => new DiscoverPage()); defineDelayedExport('headerPage', () => new HeaderPage()); defineDelayedExport('settingsPage', () => new SettingsPage()); defineDelayedExport('visualizePage', () => new VisualizePage()); +defineDelayedExport('dashboardPage', () => new DashboardPage()); defineDelayedExport('shieldPage', () => new ShieldPage()); defineDelayedExport('consolePage', () => new ConsolePage()); +defineDelayedExport('elasticDump', () => new ElasticDump()); // creates an export for values that aren't actually avaialable until // until tests start to run. These getters will throw errors if the export diff --git a/test/support/pages/common.js b/test/support/pages/common.js index 169b190059a6a..2a0e9da658500 100644 --- a/test/support/pages/common.js +++ b/test/support/pages/common.js @@ -1,4 +1,4 @@ -import { common, config, defaultTryTimeout, defaultFindTimeout, remote, shieldPage } from '../'; +import { config, defaultTryTimeout, defaultFindTimeout, remote, shieldPage } from '../'; export default (function () { var Promise = require('bluebird'); @@ -280,6 +280,7 @@ export default (function () { .setFindTimeout(defaultFindTimeout) .findDisplayedByCssSelector(testSubjSelector(selector)); } + }; return Common; diff --git a/test/support/pages/dashboard_page.js b/test/support/pages/dashboard_page.js new file mode 100644 index 0000000000000..88b8cb8f9a127 --- /dev/null +++ b/test/support/pages/dashboard_page.js @@ -0,0 +1,200 @@ +import { remote, common, defaultFindTimeout } from '../'; + +export default (function () { + var thisTime; + + function DashboardPage() { + this.remote = remote; + thisTime = this.remote.setFindTimeout(defaultFindTimeout); + } + + DashboardPage.prototype = { + constructor: DashboardPage, + + clickNewDashboard: function clickNewDashboard() { + return thisTime + .findByCssSelector('button.ng-scope[aria-label="New Dashboard"]') + .click(); + }, + + clickAddVisualization: function clickAddVisualization() { + return thisTime + .findByCssSelector('button.ng-scope[aria-label="Add a panel to the dashboard"]') + .click(); + }, + + filterVizNames: function filterVizNames(vizName) { + return thisTime + .findByCssSelector('input[placeholder="Visualizations Filter..."]') + .click() + .pressKeys(vizName); + }, + + clickVizNameLink: function clickVizNameLink(vizName) { + return thisTime + .findByLinkText(vizName) + .click(); + }, + + closeAddVizualizationPanel: function closeAddVizualizationPanel() { + common.debug('-------------close panel'); + return thisTime + .findByCssSelector('i.fa fa-chevron-up') + .click(); + }, + + addVisualization: function addVisualization(vizName) { + var self = this; + return this.clickAddVisualization() + .then(function () { + common.debug('filter visualization (' + vizName + ')'); + return self.filterVizNames(vizName); + }) + // this second wait is usually enough to avoid the + // 'stale element reference: element is not attached to the page document' + // on the next step + .then(function () { + return common.sleep(1000); + }) + .then(function () { + // but wrap in a try loop since it can still happen + return common.try(function () { + common.debug('click visualization (' + vizName + ')'); + return self.clickVizNameLink(vizName); + }); + }) + // this second click of 'Add' collapses the Add Visualization pane + .then(function () { + return self.clickAddVisualization(); + }); + }, + + saveDashboard: function saveDashboard(dashName) { + var self = this; + return thisTime + .findByCssSelector('button.ng-scope[aria-label="Save Dashboard"]') + .click() + .then(function () { + return common.sleep(1000); + }) + .then(function () { + common.debug('saveButton button clicked'); + return thisTime + .findById('dashboardTitle') + .type(dashName); + }) + // click save button + .then(function () { + return thisTime + .findByCssSelector('.btn-primary') + .click(); + }) + // verify that green message at the top of the page. + // it's only there for about 5 seconds + .then(function () { + return thisTime + .findByCssSelector('kbn-truncated.toast-message.ng-isolate-scope') + .getVisibleText(); + }); + }, + + clickDashboardByLinkText: function clickDashboardByLinkText(dashName) { + return thisTime + .findByLinkText(dashName) + .click(); + }, + + // use the search filter box to narrow the results down to a single + // entry, or at least to a single page of results + loadSavedDashboard: function loadSavedDashboard(dashName) { + var self = this; + return thisTime + .findByCssSelector('button.ng-scope[aria-label="Load Saved Dashboard"]') + .click() + .then(function filterDashboard() { + common.debug('Load Saved Dashboard button clicked'); + return self.remote + .findByCssSelector('input[name="filter"]') + .click() + .type(dashName.replace('-',' ')); + }) + .then(function () { + return common.sleep(1000); + }) + .then(function clickDashboardByLinkedText() { + return self + .clickDashboardByLinkText(dashName); + }); + }, + + + getPanelTitles: function getPanelTitles() { + common.debug('in getPanelTitles'); + return thisTime + .findAllByCssSelector('span.panel-title') + .then(function (titleObjects) { + + function getTitles(chart) { + return chart.getAttribute('title'); + } + + var getTitlePromises = titleObjects.map(getTitles); + return Promise.all(getTitlePromises); + }); + }, + + getPanelData: function getPanelData() { + common.debug('in getPanelData'); + return thisTime + .findAllByCssSelector('li.gs-w') + .then(function (titleObjects) { + + function getTitles(chart) { + var obj = {}; + return chart.getAttribute('data-col') + .then(function (theData) { + obj = {dataCol:theData}; + return chart; + }) + .then(function (chart) { + return chart.getAttribute('data-row') + .then(function (theData) { + obj.dataRow = theData; + return chart; + }); + }) + .then(function (chart) { + return chart.getAttribute('data-sizex') + .then(function (theData) { + obj.dataSizeX = theData; + return chart; + }); + }) + .then(function (chart) { + return chart.getAttribute('data-sizey') + .then(function (theData) { + obj.dataSizeY = theData; + return chart; + }); + }) + .then(function (chart) { + return chart.findByCssSelector('span.panel-title') + .then(function (titleElement) { + return titleElement.getAttribute('title'); + }) + .then(function (theData) { + obj.title = theData; + return obj; + }); + }); + } + + var getTitlePromises = titleObjects.map(getTitles); + return Promise.all(getTitlePromises); + }); + } + + }; + + return DashboardPage; +}());