Merge branch 'master' into issue4366
4
.gitignore
vendored
|
@ -12,7 +12,9 @@ target
|
|||
.idea
|
||||
*.iml
|
||||
*.log
|
||||
/test/output
|
||||
/test/screenshots/diff
|
||||
/test/screenshots/failure
|
||||
/test/screenshots/session
|
||||
/esvm
|
||||
.htpasswd
|
||||
.eslintcache
|
||||
|
|
|
@ -69,7 +69,7 @@ module.exports = function (grunt) {
|
|||
|
||||
grunt.config.merge(config);
|
||||
|
||||
config.userScriptsDir = __dirname + '/build/userScripts';
|
||||
config.packageScriptsDir = __dirname + '/tasks/build/package_scripts';
|
||||
// ensure that these run first, other configs need them
|
||||
config.services = require('./tasks/config/services')(grunt);
|
||||
config.platforms = require('./tasks/config/platforms')(grunt);
|
||||
|
|
|
@ -88,5 +88,5 @@
|
|||
# logging.verbose: false
|
||||
|
||||
# Set the interval in milliseconds to sample system and process performance
|
||||
# metrics. Minimum is 100ms. Defaults to 10000.
|
||||
# ops.interval: 10000
|
||||
# metrics. Minimum is 100ms. Defaults to 5000.
|
||||
# ops.interval: 5000
|
||||
|
|
|
@ -42,7 +42,7 @@ retrying.
|
|||
error messages.
|
||||
`logging.verbose`:: *Default: false* Set the value of this setting to `true` to log all events, including system usage
|
||||
information and all requests.
|
||||
`ops.interval`:: *Default: 10000* Set the interval in milliseconds to sample system and process performance metrics.
|
||||
`ops.interval`:: *Default: 5000* Set the interval in milliseconds to sample system and process performance metrics.
|
||||
The minimum value is 100.
|
||||
`status.allowAnonymous`:: *Default: false* If authentication is enabled, setting this to `true` allows
|
||||
unauthenticated users to access the Kibana server status API and status page.
|
||||
|
|
|
@ -59,7 +59,8 @@
|
|||
"makelogs": "makelogs",
|
||||
"mocha": "mocha",
|
||||
"mocha:debug": "mocha --debug-brk",
|
||||
"sterilize": "grunt sterilize"
|
||||
"sterilize": "grunt sterilize",
|
||||
"compareScreenshots": "node utilities/compareScreenshots"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -171,6 +172,7 @@
|
|||
"gruntify-eslint": "1.0.1",
|
||||
"html-entities": "1.1.3",
|
||||
"husky": "0.8.1",
|
||||
"image-diff": "1.6.0",
|
||||
"intern": "3.0.1",
|
||||
"istanbul-instrumenter-loader": "0.1.3",
|
||||
"karma": "0.13.9",
|
||||
|
|
|
@ -41,6 +41,7 @@ describe('plugins/elasticsearch', function () {
|
|||
upgradeDoc('4.0.0-rc1', '4.0.0', true);
|
||||
upgradeDoc('4.0.0-rc1-snapshot', '4.0.0', false);
|
||||
upgradeDoc('4.1.0-rc1-snapshot', '4.1.0-rc1', false);
|
||||
upgradeDoc('5.0.0-alpha1', '5.0.0', false);
|
||||
|
||||
it('should handle missing _id field', function () {
|
||||
let doc = {
|
||||
|
|
|
@ -91,10 +91,18 @@ describe('plugins/elasticsearch', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('should resolve with undefined if the nothing is upgradeable', function () {
|
||||
const response = { hits: { hits: [ { _id: '4.0.1-beta1' }, { _id: '4.0.0-snapshot1' } ] } };
|
||||
it('should create new config if the nothing is upgradeable', function () {
|
||||
get.withArgs('pkg.buildNum').returns(9833);
|
||||
client.create.returns(Promise.resolve());
|
||||
const response = { hits: { hits: [ { _id: '4.0.1-alpha3' }, { _id: '4.0.1-beta1' }, { _id: '4.0.0-snapshot1' } ] } };
|
||||
return upgrade(response).then(function (resp) {
|
||||
expect(resp).to.be(undefined);
|
||||
sinon.assert.calledOnce(client.create);
|
||||
const params = client.create.args[0][0];
|
||||
expect(params).to.have.property('body');
|
||||
expect(params.body).to.have.property('buildNum', 9833);
|
||||
expect(params).to.have.property('index', '.my-kibana');
|
||||
expect(params).to.have.property('type', 'config');
|
||||
expect(params).to.have.property('id', '4.0.1');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ const rcVersionRegex = /(\d+\.\d+\.\d+)\-rc(\d+)/i;
|
|||
|
||||
module.exports = function (server, doc) {
|
||||
const config = server.config();
|
||||
if (/beta|snapshot/i.test(doc._id)) return false;
|
||||
if (/alpha|beta|snapshot/i.test(doc._id)) return false;
|
||||
if (!doc._id) return false;
|
||||
if (doc._id === config.get('pkg.version')) return false;
|
||||
|
||||
|
|
|
@ -9,17 +9,21 @@ module.exports = function (server) {
|
|||
const client = server.plugins.elasticsearch.client;
|
||||
const config = server.config();
|
||||
|
||||
function createNewConfig() {
|
||||
return client.create({
|
||||
index: config.get('kibana.index'),
|
||||
type: 'config',
|
||||
body: { buildNum: config.get('pkg.buildNum') },
|
||||
id: config.get('pkg.version')
|
||||
});
|
||||
}
|
||||
|
||||
return function (response) {
|
||||
const newConfig = {};
|
||||
|
||||
// Check to see if there are any doc. If not then we set the build number and id
|
||||
if (response.hits.hits.length === 0) {
|
||||
return client.create({
|
||||
index: config.get('kibana.index'),
|
||||
type: 'config',
|
||||
body: { buildNum: config.get('pkg.buildNum') },
|
||||
id: config.get('pkg.version')
|
||||
});
|
||||
return createNewConfig();
|
||||
}
|
||||
|
||||
// if we already have a the current version in the index then we need to stop
|
||||
|
@ -30,9 +34,11 @@ module.exports = function (server) {
|
|||
if (devConfig) return Promise.resolve();
|
||||
|
||||
// Look for upgradeable configs. If none of them are upgradeable
|
||||
// then resolve with null.
|
||||
// then create a new one.
|
||||
const body = _.find(response.hits.hits, isUpgradeable.bind(null, server));
|
||||
if (!body) return Promise.resolve();
|
||||
if (!body) {
|
||||
return createNewConfig();
|
||||
}
|
||||
|
||||
// if the build number is still the template string (which it wil be in development)
|
||||
// then we need to set it to the max interger. Otherwise we will set it to the build num
|
||||
|
|
13
src/plugins/kibana/public/discover/directives/no_results.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import uiModules from 'ui/modules';
|
||||
import noResultsTemplate from '../partials/no_results.html';
|
||||
|
||||
uiModules
|
||||
.get('apps/discover')
|
||||
.directive('discoverNoResults', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: noResultsTemplate
|
||||
};
|
||||
});
|
|
@ -56,58 +56,7 @@
|
|||
|
||||
<div class="discover-wrapper col-md-10">
|
||||
<div class="discover-content">
|
||||
<!-- no results -->
|
||||
<div ng-show="resultState === 'none'">
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
|
||||
<h1>No results found <i aria-hidden="true" class="fa fa-meh-o"></i></h1>
|
||||
|
||||
<p>
|
||||
Unfortunately I could not find any results matching your search. I tried really hard. I looked all over the place and frankly, I just couldn't find anything good. Help me, help you. Here are some ideas:
|
||||
</p>
|
||||
|
||||
<div class="shard-failures" ng-show="failures">
|
||||
<h3>Shard Failures</h3>
|
||||
<p>The following shard failures ocurred:</p>
|
||||
<ul>
|
||||
<li ng-repeat="failure in failures | limitTo: failuresShown"><strong>Index:</strong> {{failure.index}} <strong>Shard:</strong> {{failure.shard}} <strong>Reason:</strong> {{failure.reason}} </li>
|
||||
</ul>
|
||||
<a ng-click="showAllFailures()" ng-if="failures.length > failuresShown" title="Show More">Show More</a>
|
||||
<a ng-click="showLessFailures()" ng-if="failures.length === failuresShown && failures.length > 5" title="Show Less">Show Less</a>
|
||||
</div>
|
||||
|
||||
<div ng-show="opts.timefield">
|
||||
<p>
|
||||
<h3>Expand your time range</h3>
|
||||
<p>I see you are looking at an index with a date field. It is possible your query does not match anything in the current time range, or that there is no data at all in the currently selected time range. Try selecting a wider time range by opening the time picker <i aria-hidden="true" class="fa fa-clock-o"></i> in the top right corner of your screen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h3>Refine your query</h3>
|
||||
<p>
|
||||
The search bar at the top uses Elasticsearch's support for Lucene <a href="http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#query-string-syntax" target="_blank">Query String syntax</a>. Let's say we're searching web server logs that have been parsed into a few fields.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<h4>Examples:</h4>
|
||||
Find requests that contain the number 200, in any field:
|
||||
<pre>200</pre>
|
||||
|
||||
Or we can search in a specific field. Find 200 in the status field:
|
||||
<pre>status:200</pre>
|
||||
|
||||
Find all status codes between 400-499:
|
||||
<pre>status:[400 TO 499]</pre>
|
||||
|
||||
Find status codes 400-499 with the extension php:
|
||||
<pre>status:[400 TO 499] AND extension:PHP</pre>
|
||||
|
||||
Or HTML
|
||||
<pre>status:[400 TO 499] AND (extension:php OR extension:html)</pre>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<discover-no-results ng-show="resultState === 'none'"></discover-no-results>
|
||||
|
||||
<!-- loading -->
|
||||
<div ng-show="resultState === 'loading'">
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'plugins/kibana/discover/saved_searches/saved_searches';
|
||||
import 'plugins/kibana/discover/directives/no_results';
|
||||
import 'plugins/kibana/discover/directives/timechart';
|
||||
import 'ui/navbar_extensions';
|
||||
import 'ui/collapsible_sidebar';
|
||||
|
|
51
src/plugins/kibana/public/discover/partials/no_results.html
Normal file
|
@ -0,0 +1,51 @@
|
|||
<div>
|
||||
<div class="col-md-10 col-md-offset-1" data-test-subj="discoverNoResults">
|
||||
|
||||
<h1>No results found <i aria-hidden="true" class="fa fa-meh-o"></i></h1>
|
||||
|
||||
<p>
|
||||
Unfortunately I could not find any results matching your search. I tried really hard. I looked all over the place and frankly, I just couldn't find anything good. Help me, help you. Here are some ideas:
|
||||
</p>
|
||||
|
||||
<div class="shard-failures" ng-show="failures">
|
||||
<h3>Shard Failures</h3>
|
||||
<p>The following shard failures ocurred:</p>
|
||||
<ul>
|
||||
<li ng-repeat="failure in failures | limitTo: failuresShown"><strong>Index:</strong> {{failure.index}} <strong>Shard:</strong> {{failure.shard}} <strong>Reason:</strong> {{failure.reason}} </li>
|
||||
</ul>
|
||||
<a ng-click="showAllFailures()" ng-if="failures.length > failuresShown" title="Show More">Show More</a>
|
||||
<a ng-click="showLessFailures()" ng-if="failures.length === failuresShown && failures.length > 5" title="Show Less">Show Less</a>
|
||||
</div>
|
||||
|
||||
<div ng-show="opts.timefield">
|
||||
<p>
|
||||
<h3>Expand your time range</h3>
|
||||
<p>I see you are looking at an index with a date field. It is possible your query does not match anything in the current time range, or that there is no data at all in the currently selected time range. Click the button below to open the time picker. For future reference you can open the time picker by clicking on the <a class="btn btn-xs navbtn" ng-click="kbnTopNav.toggle('filter')" aria-expanded="kbnTopNav.is('filter')" aria-label="time picker" data-test-subj="discoverNoResultsTimefilter"><i aria-hidden="true" class="fa fa-clock-o"></i> time picker</a> button in the top right corner of your screen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h3>Refine your query</h3>
|
||||
<p>
|
||||
The search bar at the top uses Elasticsearch's support for Lucene <a href="http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#query-string-syntax" target="_blank">Query String syntax</a>. Let's say we're searching web server logs that have been parsed into a few fields.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<h4>Examples:</h4>
|
||||
Find requests that contain the number 200, in any field:
|
||||
<pre>200</pre>
|
||||
|
||||
Or we can search in a specific field. Find 200 in the status field:
|
||||
<pre>status:200</pre>
|
||||
|
||||
Find all status codes between 400-499:
|
||||
<pre>status:[400 TO 499]</pre>
|
||||
|
||||
Find status codes 400-499 with the extension php:
|
||||
<pre>status:[400 TO 499] AND extension:PHP</pre>
|
||||
|
||||
Or HTML
|
||||
<pre>status:[400 TO 499] AND (extension:php OR extension:html)</pre>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -86,7 +86,7 @@ module.exports = () => Joi.object({
|
|||
.default(),
|
||||
|
||||
ops: Joi.object({
|
||||
interval: Joi.number().default(10000),
|
||||
interval: Joi.number().default(5000),
|
||||
}),
|
||||
|
||||
plugins: Joi.object({
|
||||
|
|
|
@ -23,7 +23,7 @@ export default function (Private) {
|
|||
this.getSourceFiltering = sinon.spy();
|
||||
this.metaFields = ['_id', '_type', '_source'];
|
||||
this.fieldFormatMap = {};
|
||||
this.routes = IndexPattern.prototype.routes;
|
||||
this.routes = IndexPattern.routes;
|
||||
|
||||
this.toIndexList = _.constant(Promise.resolve([pattern]));
|
||||
this.toDetailedIndexList = _.constant(Promise.resolve([
|
||||
|
|
|
@ -12,6 +12,7 @@ import IndexPatternsMapperProvider from 'ui/index_patterns/_mapper';
|
|||
import UtilsMappingSetupProvider from 'ui/utils/mapping_setup';
|
||||
import IndexPatternsIntervalsProvider from 'ui/index_patterns/_intervals';
|
||||
import IndexPatternsIndexPatternProvider from 'ui/index_patterns/_index_pattern';
|
||||
|
||||
describe('index pattern', function () {
|
||||
let IndexPattern;
|
||||
let mapper;
|
||||
|
@ -77,7 +78,7 @@ describe('index pattern', function () {
|
|||
|
||||
// helper function to create index patterns
|
||||
function create(id, payload) {
|
||||
let indexPattern = new IndexPattern(id);
|
||||
const indexPattern = new IndexPattern(id);
|
||||
DocSource.prototype.doIndex.returns(Promise.resolve(id));
|
||||
payload = _.defaults(payload || {}, docSourceResponse(id));
|
||||
setDocsourcePayload(payload);
|
||||
|
@ -129,16 +130,16 @@ describe('index pattern', function () {
|
|||
|
||||
describe('getScriptedFields', function () {
|
||||
it('should return all scripted fields', function () {
|
||||
let scriptedNames = _(mockLogstashFields).where({ scripted: true }).pluck('name').value();
|
||||
let respNames = _.pluck(indexPattern.getScriptedFields(), 'name');
|
||||
const scriptedNames = _(mockLogstashFields).where({ scripted: true }).pluck('name').value();
|
||||
const respNames = _.pluck(indexPattern.getScriptedFields(), 'name');
|
||||
expect(respNames).to.eql(scriptedNames);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNonScriptedFields', function () {
|
||||
it('should return all non-scripted fields', function () {
|
||||
let notScriptedNames = _(mockLogstashFields).where({ scripted: false }).pluck('name').value();
|
||||
let respNames = _.pluck(indexPattern.getNonScriptedFields(), 'name');
|
||||
const notScriptedNames = _(mockLogstashFields).where({ scripted: false }).pluck('name').value();
|
||||
const respNames = _.pluck(indexPattern.getNonScriptedFields(), 'name');
|
||||
expect(respNames).to.eql(notScriptedNames);
|
||||
});
|
||||
|
||||
|
@ -147,7 +148,7 @@ describe('index pattern', function () {
|
|||
describe('refresh fields', function () {
|
||||
// override the default indexPattern, with a truncated field list
|
||||
require('test_utils/no_digest_promises').activateForSuite();
|
||||
let indexPatternId = 'test-pattern';
|
||||
const indexPatternId = 'test-pattern';
|
||||
let indexPattern;
|
||||
let fieldLength;
|
||||
let truncatedFields;
|
||||
|
@ -181,8 +182,8 @@ describe('index pattern', function () {
|
|||
indexPattern.refreshFields(),
|
||||
])
|
||||
.then(function (data) {
|
||||
let expected = data[0]; // just the fields in the index
|
||||
let fields = indexPattern.getNonScriptedFields(); // get all but scripted fields
|
||||
const expected = data[0]; // just the fields in the index
|
||||
const fields = indexPattern.getNonScriptedFields(); // get all but scripted fields
|
||||
|
||||
expect(_.pluck(fields, 'name')).to.eql(_.pluck(expected, 'name'));
|
||||
});
|
||||
|
@ -193,15 +194,15 @@ describe('index pattern', function () {
|
|||
setDocsourcePayload(docSourceResponse(indexPatternId));
|
||||
|
||||
// add spy to indexPattern.getScriptedFields
|
||||
let scriptedFieldsSpy = sinon.spy(indexPattern, 'getScriptedFields');
|
||||
const scriptedFieldsSpy = sinon.spy(indexPattern, 'getScriptedFields');
|
||||
|
||||
// refresh fields, which will fetch
|
||||
return indexPattern.refreshFields().then(function () {
|
||||
// called to append scripted fields to the response from mapper.getFieldsForIndexPattern
|
||||
expect(scriptedFieldsSpy.callCount).to.equal(1);
|
||||
|
||||
let scripted = _.where(mockLogstashFields, { scripted: true });
|
||||
let expected = _.filter(indexPattern.fields, { scripted: true });
|
||||
const scripted = _.where(mockLogstashFields, { scripted: true });
|
||||
const expected = _.filter(indexPattern.fields, { scripted: true });
|
||||
expect(_.pluck(expected, 'name')).to.eql(_.pluck(scripted, 'name'));
|
||||
});
|
||||
});
|
||||
|
@ -210,29 +211,27 @@ describe('index pattern', function () {
|
|||
describe('add and remove scripted fields', function () {
|
||||
it('should append the scripted field', function () {
|
||||
// keep a copy of the current scripted field count
|
||||
let saveSpy = sinon.spy(indexPattern, 'save');
|
||||
let oldCount = indexPattern.getScriptedFields().length;
|
||||
const saveSpy = sinon.spy(indexPattern, 'save');
|
||||
const oldCount = indexPattern.getScriptedFields().length;
|
||||
|
||||
// add a new scripted field
|
||||
let scriptedField = {
|
||||
const scriptedField = {
|
||||
name: 'new scripted field',
|
||||
script: 'false',
|
||||
type: 'boolean'
|
||||
};
|
||||
indexPattern.addScriptedField(scriptedField.name, scriptedField.script, scriptedField.type);
|
||||
indexPattern._indexFields(); // normally triggered by docSource.onUpdate()
|
||||
|
||||
let scriptedFields = indexPattern.getScriptedFields();
|
||||
const scriptedFields = indexPattern.getScriptedFields();
|
||||
expect(saveSpy.callCount).to.equal(1);
|
||||
expect(scriptedFields).to.have.length(oldCount + 1);
|
||||
expect(indexPattern.fields.byName[scriptedField.name].displayName).to.equal(scriptedField.name);
|
||||
expect(indexPattern.fields.byName[scriptedField.name].name).to.equal(scriptedField.name);
|
||||
});
|
||||
|
||||
it('should remove scripted field, by name', function () {
|
||||
let saveSpy = sinon.spy(indexPattern, 'save');
|
||||
let scriptedFields = indexPattern.getScriptedFields();
|
||||
let oldCount = scriptedFields.length;
|
||||
let scriptedField = _.last(scriptedFields);
|
||||
const saveSpy = sinon.spy(indexPattern, 'save');
|
||||
const scriptedFields = indexPattern.getScriptedFields();
|
||||
const oldCount = scriptedFields.length;
|
||||
const scriptedField = _.last(scriptedFields);
|
||||
|
||||
indexPattern.removeScriptedField(scriptedField.name);
|
||||
|
||||
|
@ -242,8 +241,8 @@ describe('index pattern', function () {
|
|||
});
|
||||
|
||||
it('should not allow duplicate names', function () {
|
||||
let scriptedFields = indexPattern.getScriptedFields();
|
||||
let scriptedField = _.last(scriptedFields);
|
||||
const scriptedFields = indexPattern.getScriptedFields();
|
||||
const scriptedField = _.last(scriptedFields);
|
||||
expect(function () {
|
||||
indexPattern.addScriptedField(scriptedField.name, '\'new script\'', 'string');
|
||||
}).to.throwError(function (e) {
|
||||
|
@ -254,9 +253,9 @@ describe('index pattern', function () {
|
|||
|
||||
describe('popularizeField', function () {
|
||||
it('should increment the poplarity count by default', function () {
|
||||
let saveSpy = sinon.stub(indexPattern, 'save');
|
||||
const saveSpy = sinon.stub(indexPattern, 'save');
|
||||
indexPattern.fields.forEach(function (field, i) {
|
||||
let oldCount = field.count;
|
||||
const oldCount = field.count;
|
||||
|
||||
indexPattern.popularizeField(field.name);
|
||||
|
||||
|
@ -266,10 +265,10 @@ describe('index pattern', function () {
|
|||
});
|
||||
|
||||
it('should increment the poplarity count', function () {
|
||||
let saveSpy = sinon.stub(indexPattern, 'save');
|
||||
const saveSpy = sinon.stub(indexPattern, 'save');
|
||||
indexPattern.fields.forEach(function (field, i) {
|
||||
let oldCount = field.count;
|
||||
let incrementAmount = 4;
|
||||
const oldCount = field.count;
|
||||
const incrementAmount = 4;
|
||||
|
||||
indexPattern.popularizeField(field.name, incrementAmount);
|
||||
|
||||
|
@ -280,9 +279,9 @@ describe('index pattern', function () {
|
|||
|
||||
it('should decrement the poplarity count', function () {
|
||||
indexPattern.fields.forEach(function (field, i) {
|
||||
let oldCount = field.count;
|
||||
let incrementAmount = 4;
|
||||
let decrementAmount = -2;
|
||||
const oldCount = field.count;
|
||||
const incrementAmount = 4;
|
||||
const decrementAmount = -2;
|
||||
|
||||
indexPattern.popularizeField(field.name, incrementAmount);
|
||||
indexPattern.popularizeField(field.name, decrementAmount);
|
||||
|
@ -293,7 +292,7 @@ describe('index pattern', function () {
|
|||
|
||||
it('should not go below 0', function () {
|
||||
indexPattern.fields.forEach(function (field) {
|
||||
let decrementAmount = -Number.MAX_VALUE;
|
||||
const decrementAmount = -Number.MAX_VALUE;
|
||||
indexPattern.popularizeField(field.name, decrementAmount);
|
||||
expect(field.count).to.equal(0);
|
||||
});
|
||||
|
@ -311,11 +310,11 @@ describe('index pattern', function () {
|
|||
|
||||
it('invokes interval toDetailedIndexList with given start/stop times', async function () {
|
||||
await indexPattern.toDetailedIndexList(1, 2);
|
||||
let id = indexPattern.id;
|
||||
const id = indexPattern.id;
|
||||
expect(intervals.toIndexList.calledWith(id, interval, 1, 2)).to.be(true);
|
||||
});
|
||||
it('is fulfilled by the result of interval toDetailedIndexList', async function () {
|
||||
let indexList = await indexPattern.toDetailedIndexList();
|
||||
const indexList = await indexPattern.toDetailedIndexList();
|
||||
expect(indexList[0].index).to.equal('foo');
|
||||
expect(indexList[1].index).to.equal('bar');
|
||||
});
|
||||
|
@ -340,13 +339,13 @@ describe('index pattern', function () {
|
|||
|
||||
it('invokes calculateIndices with given start/stop times and sortOrder', async function () {
|
||||
await indexPattern.toDetailedIndexList(1, 2, 'sortOrder');
|
||||
let id = indexPattern.id;
|
||||
let field = indexPattern.timeFieldName;
|
||||
const id = indexPattern.id;
|
||||
const field = indexPattern.timeFieldName;
|
||||
expect(calculateIndices.calledWith(id, field, 1, 2, 'sortOrder')).to.be(true);
|
||||
});
|
||||
|
||||
it('is fulfilled by the result of calculateIndices', async function () {
|
||||
let indexList = await indexPattern.toDetailedIndexList();
|
||||
const indexList = await indexPattern.toDetailedIndexList();
|
||||
expect(indexList[0].index).to.equal('foo');
|
||||
expect(indexList[1].index).to.equal('bar');
|
||||
});
|
||||
|
@ -361,7 +360,7 @@ describe('index pattern', function () {
|
|||
});
|
||||
|
||||
it('is fulfilled by id', async function () {
|
||||
let indexList = await indexPattern.toDetailedIndexList();
|
||||
const indexList = await indexPattern.toDetailedIndexList();
|
||||
expect(indexList.index).to.equal(indexPattern.id);
|
||||
});
|
||||
});
|
||||
|
@ -372,7 +371,7 @@ describe('index pattern', function () {
|
|||
});
|
||||
|
||||
it('is fulfilled by id', async function () {
|
||||
let indexList = await indexPattern.toDetailedIndexList();
|
||||
const indexList = await indexPattern.toDetailedIndexList();
|
||||
expect(indexList.index).to.equal(indexPattern.id);
|
||||
});
|
||||
});
|
||||
|
@ -390,11 +389,11 @@ describe('index pattern', function () {
|
|||
|
||||
it('invokes interval toIndexList with given start/stop times', async function () {
|
||||
await indexPattern.toIndexList(1, 2);
|
||||
let id = indexPattern.id;
|
||||
const id = indexPattern.id;
|
||||
expect(intervals.toIndexList.calledWith(id, interval, 1, 2)).to.be(true);
|
||||
});
|
||||
it('is fulfilled by the result of interval toIndexList', async function () {
|
||||
let indexList = await indexPattern.toIndexList();
|
||||
const indexList = await indexPattern.toIndexList();
|
||||
expect(indexList[0]).to.equal('foo');
|
||||
expect(indexList[1]).to.equal('bar');
|
||||
});
|
||||
|
@ -420,13 +419,13 @@ describe('index pattern', function () {
|
|||
|
||||
it('invokes calculateIndices with given start/stop times and sortOrder', async function () {
|
||||
await indexPattern.toIndexList(1, 2, 'sortOrder');
|
||||
let id = indexPattern.id;
|
||||
let field = indexPattern.timeFieldName;
|
||||
const id = indexPattern.id;
|
||||
const field = indexPattern.timeFieldName;
|
||||
expect(calculateIndices.calledWith(id, field, 1, 2, 'sortOrder')).to.be(true);
|
||||
});
|
||||
|
||||
it('is fulfilled by the result of calculateIndices', async function () {
|
||||
let indexList = await indexPattern.toIndexList();
|
||||
const indexList = await indexPattern.toIndexList();
|
||||
expect(indexList[0]).to.equal('foo');
|
||||
expect(indexList[1]).to.equal('bar');
|
||||
});
|
||||
|
@ -442,7 +441,7 @@ describe('index pattern', function () {
|
|||
});
|
||||
|
||||
it('is fulfilled by id', async function () {
|
||||
let indexList = await indexPattern.toIndexList();
|
||||
const indexList = await indexPattern.toIndexList();
|
||||
expect(indexList).to.equal(indexPattern.id);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,24 +13,30 @@ import IndexPatternsFieldListProvider from 'ui/index_patterns/_field_list';
|
|||
import IndexPatternsFlattenHitProvider from 'ui/index_patterns/_flatten_hit';
|
||||
import IndexPatternsCalculateIndicesProvider from 'ui/index_patterns/_calculate_indices';
|
||||
import IndexPatternsPatternCacheProvider from 'ui/index_patterns/_pattern_cache';
|
||||
export default function IndexPatternFactory(Private, timefilter, Notifier, config, kbnIndex, Promise, safeConfirm) {
|
||||
let fieldformats = Private(RegistryFieldFormatsProvider);
|
||||
let getIds = Private(IndexPatternsGetIdsProvider);
|
||||
let mapper = Private(IndexPatternsMapperProvider);
|
||||
let intervals = Private(IndexPatternsIntervalsProvider);
|
||||
let DocSource = Private(DocSourceProvider);
|
||||
let mappingSetup = Private(UtilsMappingSetupProvider);
|
||||
let FieldList = Private(IndexPatternsFieldListProvider);
|
||||
|
||||
let flattenHit = Private(IndexPatternsFlattenHitProvider);
|
||||
let calculateIndices = Private(IndexPatternsCalculateIndicesProvider);
|
||||
let patternCache = Private(IndexPatternsPatternCacheProvider);
|
||||
export default function IndexPatternFactory(Private, Notifier, config, kbnIndex, Promise, safeConfirm) {
|
||||
const fieldformats = Private(RegistryFieldFormatsProvider);
|
||||
const getIds = Private(IndexPatternsGetIdsProvider);
|
||||
const mapper = Private(IndexPatternsMapperProvider);
|
||||
const intervals = Private(IndexPatternsIntervalsProvider);
|
||||
const DocSource = Private(DocSourceProvider);
|
||||
const mappingSetup = Private(UtilsMappingSetupProvider);
|
||||
const FieldList = Private(IndexPatternsFieldListProvider);
|
||||
const flattenHit = Private(IndexPatternsFlattenHitProvider);
|
||||
const calculateIndices = Private(IndexPatternsCalculateIndicesProvider);
|
||||
const patternCache = Private(IndexPatternsPatternCacheProvider);
|
||||
const type = 'index-pattern';
|
||||
const notify = new Notifier();
|
||||
const configWatchers = new WeakMap();
|
||||
const docSources = new WeakMap();
|
||||
const getRoutes = () => ({
|
||||
edit: '/settings/indices/{{id}}',
|
||||
addField: '/settings/indices/{{id}}/create-field',
|
||||
indexedFields: '/settings/indices/{{id}}?_a=(tab:indexedFields)',
|
||||
scriptedFields: '/settings/indices/{{id}}?_a=(tab:scriptedFields)'
|
||||
});
|
||||
|
||||
let type = 'index-pattern';
|
||||
|
||||
let notify = new Notifier();
|
||||
|
||||
let mapping = mappingSetup.expandShorthand({
|
||||
const mapping = mappingSetup.expandShorthand({
|
||||
title: 'string',
|
||||
timeFieldName: 'string',
|
||||
notExpandable: 'boolean',
|
||||
|
@ -39,121 +45,168 @@ export default function IndexPatternFactory(Private, timefilter, Notifier, confi
|
|||
fieldFilters: 'json',
|
||||
fieldFormatMap: {
|
||||
type: 'string',
|
||||
_serialize: function (map) {
|
||||
if (map == null) return;
|
||||
|
||||
let count = 0;
|
||||
let serialized = _.transform(map, function (flat, format, field) {
|
||||
if (!format) return;
|
||||
count++;
|
||||
flat[field] = format;
|
||||
});
|
||||
|
||||
if (count) return angular.toJson(serialized);
|
||||
_serialize(map = {}) {
|
||||
const serialized = _.transform(map, serialize);
|
||||
return _.isEmpty(serialized) ? undefined : angular.toJson(serialized);
|
||||
},
|
||||
_deserialize: function (map) {
|
||||
if (map == null) return {};
|
||||
return _.mapValues(angular.fromJson(map), function (mapping) {
|
||||
let FieldFormat = fieldformats.byId[mapping.id];
|
||||
return FieldFormat && new FieldFormat(mapping.params);
|
||||
});
|
||||
_deserialize(map = '{}') {
|
||||
return _.mapValues(angular.fromJson(map), deserialize);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function IndexPattern(id) {
|
||||
let self = this;
|
||||
function serialize(flat, format, field) {
|
||||
if (format) {
|
||||
flat[field] = format;
|
||||
}
|
||||
}
|
||||
|
||||
setId(id);
|
||||
if (!self.fieldFilters) {
|
||||
self.fieldFilters = [];
|
||||
function deserialize(mapping) {
|
||||
const FieldFormat = fieldformats.byId[mapping.id];
|
||||
return FieldFormat && new FieldFormat(mapping.params);
|
||||
}
|
||||
|
||||
function updateFromElasticSearch(indexPattern, response) {
|
||||
if (!response.found) {
|
||||
throw new errors.SavedObjectNotFound(type, indexPattern.id);
|
||||
}
|
||||
|
||||
let docSource = new DocSource();
|
||||
_.forOwn(mapping, (fieldMapping, name) => {
|
||||
if (!fieldMapping._deserialize) {
|
||||
return;
|
||||
}
|
||||
response._source[name] = fieldMapping._deserialize(
|
||||
response._source[name], response, name, fieldMapping
|
||||
);
|
||||
});
|
||||
|
||||
self.init = function () {
|
||||
// tell the docSource where to find the doc
|
||||
docSource
|
||||
// give index pattern all of the values in _source
|
||||
_.assign(indexPattern, response._source);
|
||||
|
||||
indexFields(indexPattern);
|
||||
|
||||
// any time index pattern in ES is updated, update index pattern object
|
||||
docSources
|
||||
.get(indexPattern)
|
||||
.onUpdate()
|
||||
.then(response => updateFromElasticSearch(indexPattern, response), notify.fatal);
|
||||
}
|
||||
|
||||
function indexFields(indexPattern) {
|
||||
if (!indexPattern.id) {
|
||||
return;
|
||||
}
|
||||
if (!indexPattern.fields) {
|
||||
return indexPattern.refreshFields();
|
||||
}
|
||||
initFields(indexPattern);
|
||||
}
|
||||
|
||||
function setId(indexPattern, id) {
|
||||
indexPattern.id = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
function watch(indexPattern) {
|
||||
if (configWatchers.has(indexPattern)) {
|
||||
return;
|
||||
}
|
||||
const unwatch = config.watchAll(() => {
|
||||
if (indexPattern.fields) {
|
||||
initFields(indexPattern); // re-init fields when config changes, but only if we already had fields
|
||||
}
|
||||
});
|
||||
configWatchers.set(indexPattern, { unwatch });
|
||||
}
|
||||
|
||||
function unwatch(indexPattern) {
|
||||
if (!configWatchers.has(indexPattern)) {
|
||||
return;
|
||||
}
|
||||
configWatchers.get(indexPattern).unwatch();
|
||||
configWatchers.delete(indexPattern);
|
||||
}
|
||||
|
||||
function initFields(indexPattern, input) {
|
||||
const oldValue = indexPattern.fields;
|
||||
const newValue = input || oldValue || [];
|
||||
indexPattern.fields = new FieldList(indexPattern, newValue);
|
||||
}
|
||||
|
||||
function fetchFields(indexPattern) {
|
||||
return mapper
|
||||
.getFieldsForIndexPattern(indexPattern, true)
|
||||
.then(fields => {
|
||||
const scripted = indexPattern.getScriptedFields();
|
||||
const all = fields.concat(scripted);
|
||||
initFields(indexPattern, all);
|
||||
});
|
||||
}
|
||||
|
||||
class IndexPattern {
|
||||
constructor(id) {
|
||||
setId(this, id);
|
||||
if (!this.fieldFilters) {
|
||||
this.fieldFilters = [];
|
||||
}
|
||||
docSources.set(this, new DocSource());
|
||||
|
||||
this.metaFields = config.get('metaFields');
|
||||
this.getComputedFields = getComputedFields.bind(this);
|
||||
|
||||
this.flattenHit = flattenHit(this);
|
||||
this.formatHit = formatHit(this, fieldformats.getDefaultInstance('string'));
|
||||
this.formatField = this.formatHit.formatField;
|
||||
}
|
||||
|
||||
get routes() {
|
||||
return getRoutes();
|
||||
}
|
||||
|
||||
init() {
|
||||
docSources
|
||||
.get(this)
|
||||
.index(kbnIndex)
|
||||
.type(type)
|
||||
.id(self.id);
|
||||
.id(this.id);
|
||||
|
||||
// listen for config changes and update field list
|
||||
config.watchAll(() => {
|
||||
if (self.fields) {
|
||||
// re-init fields when config changes, but only if we already had fields
|
||||
initFields();
|
||||
watch(this);
|
||||
|
||||
return mappingSetup
|
||||
.isDefined(type)
|
||||
.then(defined => {
|
||||
if (defined) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return mappingSetup.isDefined(type)
|
||||
.then(function (defined) {
|
||||
// create mapping for this type if one does not exist
|
||||
if (defined) return true;
|
||||
return mappingSetup.setup(type, mapping);
|
||||
})
|
||||
.then(function () {
|
||||
// If there is no id, then there is no document to fetch from elasticsearch
|
||||
if (!self.id) return;
|
||||
|
||||
// fetch the object from ES
|
||||
return docSource.fetch()
|
||||
.then(function applyESResp(resp) {
|
||||
if (!resp.found) throw new errors.SavedObjectNotFound(type, self.id);
|
||||
|
||||
// deserialize any json fields
|
||||
_.forOwn(mapping, function ittr(fieldMapping, name) {
|
||||
if (fieldMapping._deserialize) {
|
||||
resp._source[name] = fieldMapping._deserialize(resp._source[name], resp, name, fieldMapping);
|
||||
}
|
||||
});
|
||||
|
||||
// Give obj all of the values in _source
|
||||
_.assign(self, resp._source);
|
||||
|
||||
self._indexFields();
|
||||
|
||||
// Any time obj is updated, re-call applyESResp
|
||||
docSource.onUpdate().then(applyESResp, notify.fatal);
|
||||
});
|
||||
.then(() => {
|
||||
if (!this.id) {
|
||||
return; // no id === no elasticsearch document
|
||||
}
|
||||
return docSources.get(this)
|
||||
.fetch()
|
||||
.then(response => updateFromElasticSearch(this, response));
|
||||
})
|
||||
.then(function () {
|
||||
// return our obj as the result of init()
|
||||
return self;
|
||||
});
|
||||
};
|
||||
|
||||
function initFields(fields) {
|
||||
self.fields = new FieldList(self, fields || self.fields || []);
|
||||
.then(() => this);
|
||||
}
|
||||
|
||||
self._indexFields = function () {
|
||||
if (self.id) {
|
||||
if (!self.fields) {
|
||||
return self.refreshFields();
|
||||
} else {
|
||||
initFields();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Get the source filtering configuration for that index.
|
||||
self.getSourceFiltering = function () {
|
||||
getSourceFiltering() {
|
||||
return {
|
||||
exclude: self.fieldFilters.map(filter => filter.value)
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
self.addScriptedField = function (name, script, type, lang) {
|
||||
type = type || 'string';
|
||||
addScriptedField(name, script, type = 'string', lang) {
|
||||
const scriptedFields = this.getScriptedFields();
|
||||
const names = _.pluck(scriptedFields, 'name');
|
||||
|
||||
let scriptFields = _.pluck(self.getScriptedFields(), 'name');
|
||||
|
||||
if (_.contains(scriptFields, name)) {
|
||||
if (_.contains(names, name)) {
|
||||
throw new errors.DuplicateField(name);
|
||||
}
|
||||
|
||||
self.fields.push({
|
||||
this.fields.push({
|
||||
name: name,
|
||||
script: script,
|
||||
type: type,
|
||||
|
@ -161,186 +214,164 @@ export default function IndexPatternFactory(Private, timefilter, Notifier, confi
|
|||
lang: lang
|
||||
});
|
||||
|
||||
self.save();
|
||||
};
|
||||
this.save();
|
||||
}
|
||||
|
||||
self.removeScriptedField = function (name) {
|
||||
let fieldIndex = _.findIndex(self.fields, {
|
||||
removeScriptedField(name) {
|
||||
const fieldIndex = _.findIndex(this.fields, {
|
||||
name: name,
|
||||
scripted: true
|
||||
});
|
||||
this.fields.splice(fieldIndex, 1);
|
||||
this.save();
|
||||
}
|
||||
|
||||
self.fields.splice(fieldIndex, 1);
|
||||
|
||||
self.save();
|
||||
};
|
||||
|
||||
self.popularizeField = function (fieldName, unit) {
|
||||
if (unit == null) unit = 1;
|
||||
|
||||
let field = _.get(self, ['fields', 'byName', fieldName]);
|
||||
if (!field) return;
|
||||
|
||||
let count = Math.max((field.count || 0) + unit, 0);
|
||||
if (field.count !== count) {
|
||||
field.count = count;
|
||||
self.save();
|
||||
popularizeField(fieldName, unit = 1) {
|
||||
const field = _.get(this, ['fields', 'byName', fieldName]);
|
||||
if (!field) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
const count = Math.max((field.count || 0) + unit, 0);
|
||||
if (field.count === count) {
|
||||
return;
|
||||
}
|
||||
field.count = count;
|
||||
this.save();
|
||||
}
|
||||
|
||||
self.getNonScriptedFields = function () {
|
||||
return _.where(self.fields, { scripted: false });
|
||||
};
|
||||
getNonScriptedFields() {
|
||||
return _.where(this.fields, { scripted: false });
|
||||
}
|
||||
|
||||
self.getScriptedFields = function () {
|
||||
return _.where(self.fields, { scripted: true });
|
||||
};
|
||||
getScriptedFields() {
|
||||
return _.where(this.fields, { scripted: true });
|
||||
}
|
||||
|
||||
self.getInterval = function () {
|
||||
getInterval() {
|
||||
return this.intervalName && _.find(intervals, { name: this.intervalName });
|
||||
};
|
||||
}
|
||||
|
||||
self.toIndexList = function (start, stop, sortDirection) {
|
||||
return self
|
||||
.toDetailedIndexList(start, stop, sortDirection)
|
||||
.then(function (detailedIndices) {
|
||||
if (!_.isArray(detailedIndices)) {
|
||||
return detailedIndices.index;
|
||||
toIndexList(start, stop, sortDirection) {
|
||||
return this
|
||||
.toDetailedIndexList(start, stop, sortDirection)
|
||||
.then(detailedIndices => {
|
||||
if (!_.isArray(detailedIndices)) {
|
||||
return detailedIndices.index;
|
||||
}
|
||||
return _.pluck(detailedIndices, 'index');
|
||||
});
|
||||
}
|
||||
|
||||
toDetailedIndexList(start, stop, sortDirection) {
|
||||
return Promise.resolve().then(() => {
|
||||
const interval = this.getInterval();
|
||||
if (interval) {
|
||||
return intervals.toIndexList(
|
||||
this.id, interval, start, stop, sortDirection
|
||||
);
|
||||
}
|
||||
|
||||
return _.pluck(detailedIndices, 'index');
|
||||
if (this.isWildcard() && this.hasTimeField() && this.canExpandIndices()) {
|
||||
return calculateIndices(
|
||||
this.id, this.timeFieldName, start, stop, sortDirection
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
index: this.id,
|
||||
min: -Infinity,
|
||||
max: Infinity
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
self.toDetailedIndexList = Promise.method(function (start, stop, sortDirection) {
|
||||
let interval = self.getInterval();
|
||||
|
||||
if (interval) {
|
||||
return intervals.toIndexList(self.id, interval, start, stop, sortDirection);
|
||||
}
|
||||
|
||||
if (self.isWildcard() && self.hasTimeField() && self.canExpandIndices()) {
|
||||
return calculateIndices(self.id, self.timeFieldName, start, stop, sortDirection);
|
||||
}
|
||||
|
||||
return {
|
||||
index: self.id,
|
||||
min: -Infinity,
|
||||
max: Infinity,
|
||||
};
|
||||
});
|
||||
|
||||
self.canExpandIndices = function () {
|
||||
canExpandIndices() {
|
||||
return !this.notExpandable;
|
||||
};
|
||||
}
|
||||
|
||||
self.hasTimeField = function () {
|
||||
hasTimeField() {
|
||||
return !!(this.timeFieldName && this.fields.byName[this.timeFieldName]);
|
||||
};
|
||||
}
|
||||
|
||||
self.isWildcard = function () {
|
||||
isWildcard() {
|
||||
return _.includes(this.id, '*');
|
||||
};
|
||||
}
|
||||
|
||||
self.prepBody = function () {
|
||||
let body = {};
|
||||
prepBody() {
|
||||
const body = {};
|
||||
|
||||
// serialize json fields
|
||||
_.forOwn(mapping, function (fieldMapping, fieldName) {
|
||||
if (self[fieldName] != null) {
|
||||
_.forOwn(mapping, (fieldMapping, fieldName) => {
|
||||
if (this[fieldName] != null) {
|
||||
body[fieldName] = (fieldMapping._serialize)
|
||||
? fieldMapping._serialize(self[fieldName])
|
||||
: self[fieldName];
|
||||
? fieldMapping._serialize(this[fieldName])
|
||||
: this[fieldName];
|
||||
}
|
||||
});
|
||||
|
||||
// ensure that the docSource has the current self.id
|
||||
docSource.id(self.id);
|
||||
// ensure that the docSource has the current this.id
|
||||
docSources.get(this).id(this.id);
|
||||
|
||||
// clear the indexPattern list cache
|
||||
getIds.clearCache();
|
||||
return body;
|
||||
};
|
||||
|
||||
function setId(id) {
|
||||
return self.id = id;
|
||||
}
|
||||
|
||||
self.create = function () {
|
||||
let body = self.prepBody();
|
||||
return docSource.doCreate(body)
|
||||
.then(setId)
|
||||
.catch(function (err) {
|
||||
if (_.get(err, 'origError.status') === 409) {
|
||||
let confirmMessage = 'Are you sure you want to overwrite this?';
|
||||
|
||||
return safeConfirm(confirmMessage).then(
|
||||
function () {
|
||||
return Promise.try(function () {
|
||||
const cached = patternCache.get(self.id);
|
||||
if (cached) {
|
||||
return cached.then(pattern => pattern.destroy());
|
||||
}
|
||||
})
|
||||
.then(() => docSource.doIndex(body))
|
||||
.then(setId);
|
||||
},
|
||||
_.constant(false) // if the user doesn't overwrite, resolve with false
|
||||
);
|
||||
create() {
|
||||
const body = this.prepBody();
|
||||
return docSources.get(this)
|
||||
.doCreate(body)
|
||||
.then(id => setId(this, id))
|
||||
.catch(err => {
|
||||
if (_.get(err, 'origError.status') !== 409) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
return Promise.resolve(false);
|
||||
const confirmMessage = 'Are you sure you want to overwrite this?';
|
||||
|
||||
return safeConfirm(confirmMessage)
|
||||
.then(() => Promise
|
||||
.try(() => {
|
||||
const cached = patternCache.get(this.id);
|
||||
if (cached) {
|
||||
return cached.then(pattern => pattern.destroy());
|
||||
}
|
||||
})
|
||||
.then(() => docSources.get(this).doIndex(body))
|
||||
.then(id => setId(this, id)),
|
||||
_.constant(false) // if the user doesn't overwrite, resolve with false
|
||||
);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
self.save = function () {
|
||||
let body = self.prepBody();
|
||||
return docSource.doIndex(body).then(setId);
|
||||
};
|
||||
save() {
|
||||
const body = this.prepBody();
|
||||
return docSources.get(this)
|
||||
.doIndex(body)
|
||||
.then(id => setId(this, id));
|
||||
}
|
||||
|
||||
self.refreshFields = function () {
|
||||
return mapper.clearCache(self)
|
||||
.then(self._fetchFields)
|
||||
.then(self.save);
|
||||
};
|
||||
refreshFields() {
|
||||
return mapper
|
||||
.clearCache(this)
|
||||
.then(() => fetchFields(this))
|
||||
.then(() => this.save());
|
||||
}
|
||||
|
||||
self._fetchFields = function () {
|
||||
return mapper.getFieldsForIndexPattern(self, true)
|
||||
.then(function (fields) {
|
||||
// append existing scripted fields
|
||||
fields = fields.concat(self.getScriptedFields());
|
||||
toJSON() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
// initialize self.field with this field list
|
||||
initFields(fields);
|
||||
});
|
||||
};
|
||||
toString() {
|
||||
return '' + this.toJSON();
|
||||
}
|
||||
|
||||
self.toJSON = function () {
|
||||
return self.id;
|
||||
};
|
||||
|
||||
self.toString = function () {
|
||||
return '' + self.toJSON();
|
||||
};
|
||||
|
||||
self.destroy = function () {
|
||||
patternCache.clear(self.id);
|
||||
docSource.destroy();
|
||||
};
|
||||
|
||||
self.metaFields = config.get('metaFields');
|
||||
self.getComputedFields = getComputedFields.bind(self);
|
||||
|
||||
self.flattenHit = flattenHit(self);
|
||||
self.formatHit = formatHit(self, fieldformats.getDefaultInstance('string'));
|
||||
self.formatField = self.formatHit.formatField;
|
||||
destroy() {
|
||||
unwatch(this);
|
||||
patternCache.clear(this.id);
|
||||
docSources.get(this).destroy();
|
||||
docSources.delete(this);
|
||||
}
|
||||
}
|
||||
|
||||
IndexPattern.prototype.routes = {
|
||||
edit: '/settings/indices/{{id}}',
|
||||
addField: '/settings/indices/{{id}}/create-field',
|
||||
indexedFields: '/settings/indices/{{id}}?_a=(tab:indexedFields)',
|
||||
scriptedFields: '/settings/indices/{{id}}?_a=(tab:scriptedFields)'
|
||||
};
|
||||
|
||||
return IndexPattern;
|
||||
};
|
||||
|
|
|
@ -29,7 +29,7 @@ paginate {
|
|||
|
||||
a {
|
||||
text-decoration: none;
|
||||
background-color: @white;
|
||||
background-color: @kibanaGray6;
|
||||
margin-left: 2px;
|
||||
padding: 8px 11px;
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ paginate {
|
|||
text-decoration: none !important;
|
||||
font-weight: bold;
|
||||
color: @paginate-page-link-active-color;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,10 @@
|
|||
margin: 0 10px;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
// singular tabs are treated as titles
|
||||
> li:only-child > a {
|
||||
color: @kibanaGray1;
|
||||
}
|
||||
// Active, hover state for the getTabs
|
||||
> .active > a,
|
||||
> .active > a:hover,
|
||||
|
|
|
@ -1,55 +1,74 @@
|
|||
module.exports = function (grunt) {
|
||||
const { resolve } = require('path');
|
||||
const { indexBy } = require('lodash');
|
||||
import { resolve } from 'path';
|
||||
import { indexBy } from 'lodash';
|
||||
import exec from '../utils/exec';
|
||||
|
||||
const { config } = grunt;
|
||||
const exec = require('../utils/exec');
|
||||
const targetDir = config.get('target');
|
||||
const version = config.get('pkg.version');
|
||||
const userScriptsDir = config.get('userScriptsDir');
|
||||
const servicesByName = indexBy(config.get('services'), 'name');
|
||||
export default (grunt) => {
|
||||
const targetDir = grunt.config.get('target');
|
||||
const packageScriptsDir = grunt.config.get('packageScriptsDir');
|
||||
const servicesByName = indexBy(grunt.config.get('services'), 'name');
|
||||
const config = grunt.config.get('packages');
|
||||
const fpm = args => exec('fpm', args);
|
||||
|
||||
grunt.registerTask('_build:osPackages', function () {
|
||||
grunt.config.get('platforms').forEach(({ name, buildDir }) => {
|
||||
// TODO(sissel): Check if `fpm` is available
|
||||
if (!(/linux-x(86|64)$/.test(name))) return;
|
||||
|
||||
const arch = /x64$/.test(name) ? 'x86_64' : 'i386';
|
||||
const fpm = args => exec('fpm', args);
|
||||
|
||||
const args = [
|
||||
grunt.config.get('platforms')
|
||||
.filter(({ name }) => /linux-x(86|64)$/.test(name))
|
||||
.map(({ name, buildDir }) => {
|
||||
const architecture = /x64$/.test(name) ? 'x86_64' : 'i386';
|
||||
return {
|
||||
buildDir,
|
||||
architecture
|
||||
};
|
||||
})
|
||||
.forEach(({ buildDir, architecture }) => {
|
||||
const baseOptions = [
|
||||
'--force',
|
||||
'--package', targetDir,
|
||||
'-s', 'dir', // input type
|
||||
'--name', 'kibana',
|
||||
'--description', 'Explore\ and\ visualize\ your\ Elasticsearch\ data.',
|
||||
'--version', version,
|
||||
'--url', 'https://www.elastic.co',
|
||||
'--vendor', 'Elasticsearch,\ Inc.',
|
||||
'--maintainer', 'Kibana Team\ \<info@elastic.co\>',
|
||||
'--license', 'Apache\ 2.0',
|
||||
'--after-install', resolve(userScriptsDir, 'installer.sh'),
|
||||
'--after-remove', resolve(userScriptsDir, 'remover.sh'),
|
||||
'--config-files', '/opt/kibana/config/kibana.yml'
|
||||
'--architecture', architecture,
|
||||
'--name', config.name,
|
||||
'--description', config.description,
|
||||
'--version', config.version,
|
||||
'--url', config.site,
|
||||
'--vendor', config.vendor,
|
||||
'--maintainer', config.maintainer,
|
||||
'--license', config.license,
|
||||
'--after-install', resolve(packageScriptsDir, 'post_install.sh'),
|
||||
'--before-install', resolve(packageScriptsDir, 'pre_install.sh'),
|
||||
'--before-remove', resolve(packageScriptsDir, 'pre_remove.sh'),
|
||||
'--after-remove', resolve(packageScriptsDir, 'post_remove.sh'),
|
||||
'--config-files', config.path.kibanaConfig,
|
||||
'--template-value', `user=${config.user}`,
|
||||
'--template-value', `group=${config.group}`,
|
||||
'--template-value', `optimizeDir=${config.path.home}/optimize`,
|
||||
|
||||
//config folder is moved to path.conf, exclude {path.home}/config
|
||||
//uses relative path to --prefix, strip the leading /
|
||||
'--exclude', `${config.path.home.slice(1)}/config`
|
||||
];
|
||||
const debOptions = [
|
||||
'-t', 'deb',
|
||||
'--deb-priority', 'optional'
|
||||
];
|
||||
const rpmOptions = [
|
||||
'-t', 'rpm',
|
||||
'--rpm-os', 'linux'
|
||||
];
|
||||
const args = [
|
||||
`${buildDir}/=${config.path.home}/`,
|
||||
`${buildDir}/config/=${config.path.conf}/`,
|
||||
`${servicesByName.sysv.outputDir}/etc/=/etc/`,
|
||||
`${servicesByName.systemd.outputDir}/lib/=/lib/`
|
||||
];
|
||||
|
||||
const files = buildDir + '/=/opt/kibana';
|
||||
const sysv = servicesByName.sysv.outputDir + '/etc/=/etc/';
|
||||
const systemd = servicesByName.systemd.outputDir + '/lib/=/lib/';
|
||||
|
||||
//Manually find flags, multiple args without assignment are not entirely parsed
|
||||
var flags = grunt.option.flags().join(',');
|
||||
|
||||
const buildDeb = !!flags.match('deb');
|
||||
const buildRpm = !!flags.match('rpm');
|
||||
const noneSpecified = !buildRpm && !buildDeb;
|
||||
|
||||
grunt.file.mkdir(targetDir);
|
||||
if (buildDeb || noneSpecified) {
|
||||
fpm(args.concat('-t', 'deb', '--deb-priority', 'optional', '-a', arch, files, sysv, systemd));
|
||||
const flags = grunt.option.flags().filter(flag => /deb|rpm/.test(flag)).join(',');
|
||||
const buildDeb = flags.includes('deb') || !flags.length;
|
||||
const buildRpm = flags.includes('rpm') || !flags.length;
|
||||
if (buildDeb) {
|
||||
fpm([...baseOptions, ...debOptions, ...args]);
|
||||
}
|
||||
if (buildRpm || noneSpecified) {
|
||||
fpm(args.concat('-t', 'rpm', '-a', arch, '--rpm-os', 'linux', files, sysv, systemd));
|
||||
if (buildRpm) {
|
||||
fpm([...baseOptions, ...rpmOptions, ...args]);
|
||||
}
|
||||
|
||||
});
|
||||
|
|
17
tasks/build/package_scripts/post_install.sh
Normal file
|
@ -0,0 +1,17 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
user_check() {
|
||||
getent passwd "$1" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
user_create() {
|
||||
# Create a system user. A system user is one within the system uid range and
|
||||
# has no expiration
|
||||
useradd -r "$1"
|
||||
}
|
||||
|
||||
if ! user_check "<%= user %>" ; then
|
||||
user_create "<%= user %>"
|
||||
fi
|
||||
chown -R <%= user %>:<%= group %> <%= optimizeDir %>
|
42
tasks/build/package_scripts/post_remove.sh
Normal file
|
@ -0,0 +1,42 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
user_check() {
|
||||
getent passwd "$1" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
user_remove() {
|
||||
userdel "$1"
|
||||
}
|
||||
|
||||
REMOVE_USER=false
|
||||
|
||||
case $1 in
|
||||
# Includes cases for all valid arguments, exit 1 otherwise
|
||||
# Debian
|
||||
purge)
|
||||
REMOVE_USER=true
|
||||
;;
|
||||
|
||||
remove|failed-upgrade|abort-install|abort-upgrade|disappear|upgrade|disappear)
|
||||
;;
|
||||
|
||||
# Red Hat
|
||||
0)
|
||||
REMOVE_USER=true
|
||||
;;
|
||||
|
||||
1)
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "post remove script called with unknown argument \`$1'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$REMOVE_USER" = "true" ]; then
|
||||
if user_check "<%= user %>" ; then
|
||||
user_remove "<%= user %>"
|
||||
fi
|
||||
fi
|
14
tasks/build/package_scripts/pre_install.sh
Normal file
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
if command -v systemctl >/dev/null && systemctl is-active kibana.service >/dev/null; then
|
||||
systemctl --no-reload stop kibana.service
|
||||
elif [ -x /etc/init.d/kibana ]; then
|
||||
if command -v invoke-rc.d >/dev/null; then
|
||||
invoke-rc.d kibana stop
|
||||
elif command -v service >/dev/null; then
|
||||
service kibana stop
|
||||
else
|
||||
/etc/init.d/kibana stop
|
||||
fi
|
||||
fi
|
16
tasks/build/package_scripts/pre_remove.sh
Normal file
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
echo -n "Stopping kibana service..."
|
||||
if command -v systemctl >/dev/null && systemctl is-active kibana.service >/dev/null; then
|
||||
systemctl --no-reload stop kibana.service
|
||||
elif [ -x /etc/init.d/kibana ]; then
|
||||
if command -v invoke-rc.d >/dev/null; then
|
||||
invoke-rc.d kibana stop
|
||||
elif command -v service >/dev/null; then
|
||||
service kibana stop
|
||||
else
|
||||
/etc/init.d/kibana stop
|
||||
fi
|
||||
fi
|
||||
echo " OK"
|
|
@ -1,31 +1,29 @@
|
|||
module.exports = function createServices(grunt) {
|
||||
const { resolve } = require('path');
|
||||
const { appendFileSync } = require('fs');
|
||||
const exec = require('../utils/exec');
|
||||
import { resolve } from 'path';
|
||||
import { appendFileSync } from 'fs';
|
||||
import exec from '../utils/exec';
|
||||
|
||||
export default (grunt) => {
|
||||
const userScriptsDir = grunt.config.get('userScriptsDir');
|
||||
const { path, user, group, name, description } = grunt.config.get('packages');
|
||||
|
||||
grunt.registerTask('_build:pleaseRun', function () {
|
||||
// TODO(sissel): Detect if 'pleaserun' is found, and provide a useful error
|
||||
// to the user if it is missing.
|
||||
|
||||
grunt.config.get('services').forEach(function (service) {
|
||||
grunt.config.get('services').forEach((service) => {
|
||||
grunt.file.mkdir(service.outputDir);
|
||||
exec('pleaserun', [
|
||||
'--install',
|
||||
'--no-install-actions',
|
||||
'--install-prefix', service.outputDir,
|
||||
'--overwrite',
|
||||
'--user', 'kibana',
|
||||
'--group', 'kibana',
|
||||
'--sysv-log-path', '/var/log/kibana/',
|
||||
'--name', name,
|
||||
'--description', description,
|
||||
'--user', user,
|
||||
'--group', group,
|
||||
'--sysv-log-path', path.logs,
|
||||
'-p', service.name,
|
||||
'-v', service.version,
|
||||
'/opt/kibana/bin/kibana'
|
||||
path.kibanaBin,
|
||||
`-c ${path.kibanaConfig}`
|
||||
]);
|
||||
});
|
||||
|
||||
grunt.file.mkdir(userScriptsDir);
|
||||
exec('please-manage-user', ['--output', userScriptsDir, 'kibana']);
|
||||
appendFileSync(resolve(userScriptsDir, 'installer.sh'), 'chown kibana:kibana /opt/kibana/optimize');
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,20 +1,52 @@
|
|||
export default (grunt) => {
|
||||
const version = grunt.config.get('pkg.version');
|
||||
const productionPath = `kibana/${version.match(/\d\.\d/)[0]}`;
|
||||
const stagingPath = `kibana/staging/${version.match(/\d\.\d\.\d/)[0]}-XXXXXXX/repos/${version.match(/\d\./)[0]}x`;
|
||||
const rpmFolder = 'centos';
|
||||
const debFolder = 'debian';
|
||||
const VERSION = grunt.config.get('pkg.version');
|
||||
|
||||
const FOLDER_STAGING = `kibana/staging/${VERSION.match(/\d\.\d\.\d/)[0]}-XXXXXXX/repos/${VERSION.match(/\d\./)[0]}x`;
|
||||
const FOLDER_PRODUCTION = `kibana/${VERSION.match(/\d\.\d/)[0]}`;
|
||||
|
||||
const FOLDERNAME_DEB = 'debian';
|
||||
const FOLDERNAME_RPM = 'centos';
|
||||
|
||||
const PREFIX_STAGING_DEB = `${FOLDER_STAGING}/${FOLDERNAME_DEB}`;
|
||||
const PREFIX_STAGING_RPM = `${FOLDER_STAGING}/${FOLDERNAME_RPM}`;
|
||||
const PREFIX_PRODUCTION_DEB = `${FOLDER_PRODUCTION}/${FOLDERNAME_DEB}`;
|
||||
const PREFIX_PRODUCTION_RPM = `${FOLDER_PRODUCTION}/${FOLDERNAME_RPM}`;
|
||||
|
||||
const FOLDER_CONFIG = '/etc/kibana';
|
||||
const FOLDER_LOGS = '/var/log/kibana';
|
||||
const FOLDER_HOME = '/usr/share/kibana';
|
||||
|
||||
const FILE_KIBANA_CONF = `${FOLDER_CONFIG}/kibana.yml`;
|
||||
const FILE_KIBANA_BINARY = `${FOLDER_HOME}/bin/kibana`;
|
||||
|
||||
return {
|
||||
staging: {
|
||||
bucket: 'download.elasticsearch.org',
|
||||
debPrefix: `${stagingPath}/${debFolder}`,
|
||||
rpmPrefix: `${stagingPath}/${rpmFolder}`
|
||||
publish: {
|
||||
staging: {
|
||||
bucket: 'download.elasticsearch.org',
|
||||
debPrefix: PREFIX_STAGING_DEB,
|
||||
rpmPrefix: PREFIX_STAGING_RPM
|
||||
},
|
||||
production: {
|
||||
bucket: 'packages.elasticsearch.org',
|
||||
debPrefix: PREFIX_STAGING_DEB,
|
||||
rpmPrefix: PREFIX_STAGING_RPM
|
||||
}
|
||||
},
|
||||
production: {
|
||||
bucket: 'packages.elasticsearch.org',
|
||||
debPrefix: `${productionPath}/${debFolder}`,
|
||||
rpmPrefix: `${productionPath}/${rpmFolder}`
|
||||
user: 'kibana',
|
||||
group: 'kibana',
|
||||
name: 'kibana',
|
||||
description: 'Explore\ and\ visualize\ your\ Elasticsearch\ data',
|
||||
site: 'https://www.elastic.co',
|
||||
vendor: 'Elasticsearch,\ Inc.',
|
||||
maintainer: 'Kibana Team\ \<info@elastic.co\>',
|
||||
license: 'Apache\ 2.0',
|
||||
version: VERSION,
|
||||
path: {
|
||||
conf: FOLDER_CONFIG,
|
||||
logs: FOLDER_LOGS,
|
||||
home: FOLDER_HOME,
|
||||
kibanaBin: FILE_KIBANA_BINARY,
|
||||
kibanaConfig: FILE_KIBANA_CONF
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ import { promisify } from 'bluebird';
|
|||
import readline from 'readline';
|
||||
|
||||
export default (grunt) => {
|
||||
const packages = grunt.config.get('packages');
|
||||
const publishConfig = grunt.config.get('packages').publish;
|
||||
const platforms = grunt.config.get('platforms');
|
||||
|
||||
function debS3(deb) {
|
||||
|
@ -87,8 +87,8 @@ export default (grunt) => {
|
|||
if (platform.debPath) {
|
||||
debS3({
|
||||
filePath: platform.debPath,
|
||||
bucket: packages[environment].bucket,
|
||||
prefix: packages[environment].debPrefix.replace('XXXXXXX', trimmedHash),
|
||||
bucket: publishConfig[environment].bucket,
|
||||
prefix: publishConfig[environment].debPrefix.replace('XXXXXXX', trimmedHash),
|
||||
signatureKeyId: signature.id,
|
||||
arch: platform.name.match('x64') ? 'amd64' : 'i386',
|
||||
awsKey: aws.key,
|
||||
|
@ -99,8 +99,8 @@ export default (grunt) => {
|
|||
if (platform.rpmPath) {
|
||||
rpmS3({
|
||||
filePath: platform.rpmPath,
|
||||
bucket: packages[environment].bucket,
|
||||
prefix: packages[environment].rpmPrefix.replace('XXXXXXX', trimmedHash),
|
||||
bucket: publishConfig[environment].bucket,
|
||||
prefix: publishConfig[environment].rpmPrefix.replace('XXXXXXX', trimmedHash),
|
||||
signingKeyName: signature.name,
|
||||
awsKey: aws.key,
|
||||
awsSecret: aws.secret
|
||||
|
|
1
test/fixtures/dump_data/visualize.data.json
vendored
Normal file
1
test/fixtures/dump_data/visualize.mapping.json
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{".kibana":{"mappings":{"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}}}}}}}}
|
|
@ -16,28 +16,23 @@ import {
|
|||
|
||||
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-*'});
|
||||
});
|
||||
})
|
||||
var logstash = scenarioManager.loadIfEmpty('logstashFunctional');
|
||||
// delete .kibana index and update configDoc
|
||||
return esClient.deleteAndUpdateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'})
|
||||
// and load a set of makelogs data
|
||||
.then(function loadkibana4() {
|
||||
.then(function loadkibanaVisualizations() {
|
||||
common.debug('load kibana index with visualizations');
|
||||
return elasticDump.elasticLoad('dashboard','.kibana');
|
||||
})
|
||||
.then(function () {
|
||||
return scenarioManager.loadIfEmpty('logstashFunctional');
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('navigateToApp dashboard');
|
||||
return common.navigateToApp('dashboard');
|
||||
})
|
||||
// wait for the logstash data load to finish if it hasn't already
|
||||
.then(function () {
|
||||
return logstash;
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
|
@ -91,7 +86,7 @@ import {
|
|||
|
||||
bdd.it('should save and load dashboard', function saveAndLoadDashboard() {
|
||||
var testSubName = 'Dashboard Test 1';
|
||||
// save time on the dashboard?
|
||||
// TODO: save time on the dashboard and test it
|
||||
return dashboardPage.saveDashboard(testSubName)
|
||||
// click New Dashboard just to clear the one we just created
|
||||
.then(function () {
|
||||
|
|
|
@ -5,6 +5,8 @@ import {
|
|||
headerPage,
|
||||
scenarioManager,
|
||||
settingsPage,
|
||||
esClient,
|
||||
elasticDump
|
||||
} from '../../../support';
|
||||
|
||||
(function () {
|
||||
|
@ -18,20 +20,16 @@ import {
|
|||
var fromTime = '2015-09-19 06:31:44.000';
|
||||
var toTime = '2015-09-23 18:31:44.000';
|
||||
|
||||
// start each test with an empty kibana index
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
// delete .kibana index and update configDoc
|
||||
return esClient.deleteAndUpdateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'})
|
||||
.then(function loadkibanaIndexPattern() {
|
||||
common.debug('load kibana index with default index pattern');
|
||||
return elasticDump.elasticLoad('visualize','.kibana');
|
||||
})
|
||||
// and load a set of makelogs data
|
||||
.then(function loadIfEmptyMakelogs() {
|
||||
return scenarioManager.loadIfEmpty('logstashFunctional');
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('navigateTo');
|
||||
return settingsPage.navigateTo();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('createIndexPattern');
|
||||
return settingsPage.createIndexPattern();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('discover');
|
||||
return common.navigateToApp('discover');
|
||||
|
|
|
@ -5,6 +5,8 @@ import {
|
|||
discoverPage,
|
||||
settingsPage,
|
||||
headerPage,
|
||||
esClient,
|
||||
elasticDump
|
||||
} from '../../../support';
|
||||
|
||||
(function () {
|
||||
|
@ -16,20 +18,16 @@ import {
|
|||
var fromTime = '2015-09-19 06:31:44.000';
|
||||
var toTime = '2015-09-23 18:31:44.000';
|
||||
|
||||
// start each test with an empty kibana index
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
// delete .kibana index and update configDoc
|
||||
return esClient.deleteAndUpdateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'})
|
||||
.then(function loadkibanaIndexPattern() {
|
||||
common.debug('load kibana index with default index pattern');
|
||||
return elasticDump.elasticLoad('visualize','.kibana');
|
||||
})
|
||||
// and load a set of makelogs data
|
||||
.then(function loadIfEmptyMakelogs() {
|
||||
return scenarioManager.loadIfEmpty('logstashFunctional');
|
||||
})
|
||||
.then(function (navigateTo) {
|
||||
common.debug('navigateTo');
|
||||
return settingsPage.navigateTo();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('createIndexPattern');
|
||||
return settingsPage.createIndexPattern();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('discover');
|
||||
return common.navigateToApp('discover');
|
||||
|
@ -261,6 +259,12 @@ import {
|
|||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('should not show "no results"', () => {
|
||||
return discoverPage.hasNoResults().then(visible => {
|
||||
expect(visible).to.be(false);
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
function verifyChartData(expectedBarChartData) {
|
||||
return common.try(function tryingForTime() {
|
||||
|
@ -290,6 +294,69 @@ import {
|
|||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
bdd.describe('query #2, which has an empty time range', function () {
|
||||
var fromTime = '1999-06-11 09:22:11.000';
|
||||
var toTime = '1999-06-12 11:21:04.000';
|
||||
|
||||
bdd.before(() => {
|
||||
common.debug('setAbsoluteRangeForAnotherQuery');
|
||||
return headerPage
|
||||
.setAbsoluteRange(fromTime, toTime)
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('should show "no results"', () => {
|
||||
return discoverPage.hasNoResults().then(visible => {
|
||||
expect(visible).to.be(true);
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('should suggest a new time range is picked', () => {
|
||||
return discoverPage.hasNoResultsTimepicker().then(visible => {
|
||||
expect(visible).to.be(true);
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('should open and close the time picker', () => {
|
||||
let i = 0;
|
||||
|
||||
return closeTimepicker() // close
|
||||
.then(() => isTimepickerOpen(false)
|
||||
.then(el => el.click()) // open
|
||||
.then(() => isTimepickerOpen(true))
|
||||
.then(el => el.click()) // close
|
||||
.then(() => isTimepickerOpen(false))
|
||||
.catch(common.handleError(this))
|
||||
);
|
||||
|
||||
function closeTimepicker() {
|
||||
return headerPage.isTimepickerOpen().then(shown => {
|
||||
if (!shown) {
|
||||
return;
|
||||
}
|
||||
return discoverPage
|
||||
.getNoResultsTimepicker()
|
||||
.click(); // close
|
||||
});
|
||||
}
|
||||
|
||||
function isTimepickerOpen(expected) {
|
||||
return headerPage.isTimepickerOpen().then(shown => {
|
||||
common.debug(`expect (#${++i}) timepicker to be ${peek(expected)} (is ${peek(shown)}).`);
|
||||
expect(shown).to.be(expected);
|
||||
return discoverPage.getNoResultsTimepicker();
|
||||
function peek(state) {
|
||||
return state ? 'open' : 'closed';
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}());
|
||||
}());
|
||||
|
|
|
@ -4,7 +4,9 @@ import {
|
|||
discoverPage,
|
||||
headerPage,
|
||||
scenarioManager,
|
||||
settingsPage
|
||||
settingsPage,
|
||||
esClient,
|
||||
elasticDump
|
||||
} from '../../../support';
|
||||
|
||||
(function () {
|
||||
|
@ -16,20 +18,16 @@ import {
|
|||
var fromTime = '2015-09-19 06:31:44.000';
|
||||
var toTime = '2015-09-23 18:31:44.000';
|
||||
|
||||
// start each test with an empty kibana index
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
// delete .kibana index and update configDoc
|
||||
return esClient.deleteAndUpdateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'})
|
||||
.then(function loadkibanaIndexPattern() {
|
||||
common.debug('load kibana index with default index pattern');
|
||||
return elasticDump.elasticLoad('visualize','.kibana');
|
||||
})
|
||||
// and load a set of makelogs data
|
||||
.then(function loadIfEmptyMakelogs() {
|
||||
return scenarioManager.loadIfEmpty('logstashFunctional');
|
||||
})
|
||||
.then(function (navigateTo) {
|
||||
common.debug('navigateTo');
|
||||
return settingsPage.navigateTo();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('createIndexPattern');
|
||||
return settingsPage.createIndexPattern();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('discover');
|
||||
return common.navigateToApp('discover');
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
import { bdd, common, discoverPage, headerPage, settingsPage, scenarioManager } from '../../../support';
|
||||
import {
|
||||
bdd,
|
||||
common,
|
||||
discoverPage,
|
||||
headerPage,
|
||||
settingsPage,
|
||||
scenarioManager,
|
||||
esClient,
|
||||
elasticDump
|
||||
} from '../../../support';
|
||||
|
||||
(function () {
|
||||
var expect = require('expect.js');
|
||||
|
@ -18,20 +27,16 @@ import { bdd, common, discoverPage, headerPage, settingsPage, scenarioManager }
|
|||
var fromTime = '2015-09-19 06:31:44.000';
|
||||
var toTime = '2015-09-23 18:31:44.000';
|
||||
|
||||
// start each test with an empty kibana index
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
// delete .kibana index and update configDoc
|
||||
return esClient.deleteAndUpdateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'})
|
||||
.then(function loadkibanaIndexPattern() {
|
||||
common.debug('load kibana index with default index pattern');
|
||||
return elasticDump.elasticLoad('visualize','.kibana');
|
||||
})
|
||||
// and load a set of makelogs data
|
||||
.then(function loadIfEmptyMakelogs() {
|
||||
return scenarioManager.loadIfEmpty('logstashFunctional');
|
||||
})
|
||||
.then(function (navigateTo) {
|
||||
common.debug('navigateTo');
|
||||
return settingsPage.navigateTo();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('createIndexPattern');
|
||||
return settingsPage.createIndexPattern();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('discover');
|
||||
return common.navigateToApp('discover');
|
||||
|
|
|
@ -2,7 +2,8 @@ import {
|
|||
bdd,
|
||||
common,
|
||||
settingsPage,
|
||||
scenarioManager
|
||||
scenarioManager,
|
||||
esClient
|
||||
} from '../../../support';
|
||||
|
||||
(function () {
|
||||
|
@ -11,7 +12,8 @@ import {
|
|||
(function () {
|
||||
bdd.describe('creating and deleting default index', function describeIndexTests() {
|
||||
bdd.before(function () {
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
// delete .kibana index and then wait for Kibana to re-create it
|
||||
return esClient.deleteAndUpdateConfigDoc()
|
||||
.then(function () {
|
||||
return settingsPage.navigateTo();
|
||||
});
|
||||
|
@ -25,7 +27,7 @@ import {
|
|||
bdd.it('should allow setting advanced settings', function () {
|
||||
return settingsPage.clickAdvancedTab()
|
||||
.then(function TestCallSetAdvancedSettingsForTimezone() {
|
||||
common.log('calling setAdvancedSetting');
|
||||
common.debug('calling setAdvancedSetting');
|
||||
return settingsPage.setAdvancedSettings('dateFormat:tz', 'America/Phoenix');
|
||||
})
|
||||
.then(function GetAdvancedSetting() {
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
common,
|
||||
settingsPage,
|
||||
scenarioManager,
|
||||
esClient
|
||||
} from '../../../support';
|
||||
|
||||
(function () {
|
||||
|
@ -11,7 +12,8 @@ import {
|
|||
(function () {
|
||||
bdd.describe('user input reactions', function () {
|
||||
bdd.beforeEach(function () {
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
// delete .kibana index and then wait for Kibana to re-create it
|
||||
return esClient.deleteAndUpdateConfigDoc()
|
||||
.then(function () {
|
||||
return settingsPage.navigateTo();
|
||||
});
|
||||
|
|
|
@ -3,7 +3,8 @@ import {
|
|||
common,
|
||||
remote,
|
||||
scenarioManager,
|
||||
settingsPage
|
||||
settingsPage,
|
||||
esClient
|
||||
} from '../../../support';
|
||||
|
||||
(function () {
|
||||
|
@ -12,7 +13,8 @@ import {
|
|||
(function () {
|
||||
bdd.describe('creating and deleting default index', function describeIndexTests() {
|
||||
bdd.before(function () {
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
// delete .kibana index and then wait for Kibana to re-create it
|
||||
return esClient.deleteAndUpdateConfigDoc()
|
||||
.then(function () {
|
||||
return settingsPage.navigateTo();
|
||||
});
|
||||
|
|
|
@ -2,7 +2,8 @@ import {
|
|||
bdd,
|
||||
common,
|
||||
scenarioManager,
|
||||
settingsPage
|
||||
settingsPage,
|
||||
esClient
|
||||
} from '../../../support';
|
||||
|
||||
(function () {
|
||||
|
@ -11,7 +12,8 @@ import {
|
|||
(function () {
|
||||
bdd.describe('index result popularity', function describeIndexTests() {
|
||||
bdd.before(function () {
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
// delete .kibana index and then wait for Kibana to re-create it
|
||||
return esClient.deleteAndUpdateConfigDoc()
|
||||
.then(function () {
|
||||
return settingsPage.navigateTo();
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
defaultTimeout,
|
||||
settingsPage,
|
||||
scenarioManager,
|
||||
esClient
|
||||
} from '../../../support';
|
||||
|
||||
(function () {
|
||||
|
@ -12,7 +13,8 @@ import {
|
|||
(function () {
|
||||
bdd.describe('index result field sort', function describeIndexTests() {
|
||||
bdd.before(function () {
|
||||
return scenarioManager.reload('emptyKibana');
|
||||
// delete .kibana index and then wait for Kibana to re-create it
|
||||
return esClient.deleteAndUpdateConfigDoc();
|
||||
});
|
||||
|
||||
var columns = [{
|
||||
|
|
|
@ -2,7 +2,8 @@ import {
|
|||
bdd,
|
||||
common,
|
||||
scenarioManager,
|
||||
settingsPage
|
||||
settingsPage,
|
||||
esClient
|
||||
} from '../../../support';
|
||||
|
||||
(function () {
|
||||
|
@ -11,7 +12,8 @@ import {
|
|||
(function () {
|
||||
bdd.describe('initial state', function () {
|
||||
bdd.before(function () {
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
// delete .kibana index and then wait for Kibana to re-create it
|
||||
return esClient.deleteAndUpdateConfigDoc()
|
||||
.then(function () {
|
||||
return settingsPage.navigateTo();
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { bdd, defaultTimeout, scenarioManager } from '../../../support';
|
||||
import { bdd, defaultTimeout, scenarioManager, esClient, common } from '../../../support';
|
||||
|
||||
(function () {
|
||||
bdd.describe('settings app', function () {
|
||||
|
@ -7,16 +7,13 @@ import { bdd, defaultTimeout, scenarioManager } from '../../../support';
|
|||
// on setup, we create an settingsPage instance
|
||||
// that we will use for all the tests
|
||||
bdd.before(function () {
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
.then(function () {
|
||||
return scenarioManager.loadIfEmpty('makelogs');
|
||||
});
|
||||
return scenarioManager.loadIfEmpty('makelogs');
|
||||
});
|
||||
|
||||
bdd.after(function () {
|
||||
return scenarioManager.unload('makelogs')
|
||||
.then(function () {
|
||||
scenarioManager.unload('emptyKibana');
|
||||
return esClient.delete('.kibana');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -16,29 +16,8 @@ import {
|
|||
var fromTime = '2015-09-19 06:31:44.000';
|
||||
var toTime = '2015-09-23 18:31:44.000';
|
||||
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
.then(function () {
|
||||
common.debug('navigateTo');
|
||||
return settingsPage.navigateTo();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('createIndexPattern');
|
||||
return settingsPage.createIndexPattern();
|
||||
})
|
||||
.then(function () {
|
||||
return settingsPage.clickAdvancedTab();
|
||||
})
|
||||
.then(function GetAdvancedSetting() {
|
||||
common.debug('check for required UTC timezone');
|
||||
return settingsPage.getAdvancedSettings('dateFormat:tz');
|
||||
})
|
||||
.then(function (advancedSetting) {
|
||||
expect(advancedSetting).to.be('UTC');
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('navigateToApp visualize');
|
||||
return common.navigateToApp('visualize');
|
||||
})
|
||||
common.debug('navigateToApp visualize');
|
||||
return common.navigateToApp('visualize')
|
||||
.then(function () {
|
||||
common.debug('clickAreaChart');
|
||||
return visualizePage.clickAreaChart();
|
||||
|
|
|
@ -11,23 +11,11 @@ import {
|
|||
|
||||
(function () {
|
||||
bdd.describe('visualize app', function describeIndexTests() {
|
||||
bdd.before(function () {
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
.then(function () {
|
||||
common.debug('navigateTo');
|
||||
return settingsPage.navigateTo();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('createIndexPattern');
|
||||
return settingsPage.createIndexPattern();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('navigateToApp visualize');
|
||||
return common.navigateToApp('visualize');
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.before(function () {
|
||||
common.debug('navigateToApp visualize');
|
||||
return common.navigateToApp('visualize');
|
||||
});
|
||||
|
||||
bdd.describe('chart types', function indexPatternCreation() {
|
||||
|
||||
|
|
|
@ -16,29 +16,8 @@ import {
|
|||
var toTime = '2015-09-23 18:31:44.000';
|
||||
|
||||
bdd.before(function () {
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
.then(function () {
|
||||
common.debug('navigateTo');
|
||||
return settingsPage.navigateTo();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('createIndexPattern');
|
||||
return settingsPage.createIndexPattern();
|
||||
})
|
||||
.then(function () {
|
||||
return settingsPage.clickAdvancedTab();
|
||||
})
|
||||
.then(function GetAdvancedSetting() {
|
||||
common.debug('check for required UTC timezone');
|
||||
return settingsPage.getAdvancedSettings('dateFormat:tz');
|
||||
})
|
||||
.then(function (advancedSetting) {
|
||||
expect(advancedSetting).to.be('UTC');
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('navigateToApp visualize');
|
||||
return common.navigateToApp('visualize');
|
||||
})
|
||||
common.debug('navigateToApp visualize');
|
||||
return common.navigateToApp('visualize')
|
||||
.then(function () {
|
||||
common.debug('clickDataTable');
|
||||
return visualizePage.clickDataTable();
|
||||
|
|
|
@ -16,29 +16,8 @@ import {
|
|||
var fromTime = '2015-09-19 06:31:44.000';
|
||||
var toTime = '2015-09-23 18:31:44.000';
|
||||
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
.then(function () {
|
||||
common.debug('navigateTo');
|
||||
return settingsPage.navigateTo();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('createIndexPattern');
|
||||
return settingsPage.createIndexPattern();
|
||||
})
|
||||
.then(function () {
|
||||
return settingsPage.clickAdvancedTab();
|
||||
})
|
||||
.then(function GetAdvancedSetting() {
|
||||
common.debug('check for required UTC timezone');
|
||||
return settingsPage.getAdvancedSettings('dateFormat:tz');
|
||||
})
|
||||
.then(function (advancedSetting) {
|
||||
expect(advancedSetting).to.be('UTC');
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('navigateToApp visualize');
|
||||
return common.navigateToApp('visualize');
|
||||
})
|
||||
common.debug('navigateToApp visualize');
|
||||
return common.navigateToApp('visualize')
|
||||
.then(function () {
|
||||
common.debug('clickLineChart');
|
||||
return visualizePage.clickLineChart();
|
||||
|
|
|
@ -21,29 +21,8 @@ import {
|
|||
common.debug('Start of test' + testSubName + 'Visualization');
|
||||
var vizName1 = 'Visualization ' + testSubName;
|
||||
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
.then(function () {
|
||||
common.debug('navigateTo');
|
||||
return settingsPage.navigateTo();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('createIndexPattern');
|
||||
return settingsPage.createIndexPattern();
|
||||
})
|
||||
.then(function () {
|
||||
return settingsPage.clickAdvancedTab();
|
||||
})
|
||||
.then(function GetAdvancedSetting() {
|
||||
common.debug('check for required UTC timezone');
|
||||
return settingsPage.getAdvancedSettings('dateFormat:tz');
|
||||
})
|
||||
.then(function (advancedSetting) {
|
||||
expect(advancedSetting).to.be('UTC');
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('navigateToApp visualize');
|
||||
return common.navigateToApp('visualize');
|
||||
})
|
||||
common.debug('navigateToApp visualize');
|
||||
return common.navigateToApp('visualize')
|
||||
.then(function () {
|
||||
common.debug('clickMetric');
|
||||
return visualizePage.clickMetric();
|
||||
|
|
|
@ -16,29 +16,8 @@ import {
|
|||
var fromTime = '2015-09-19 06:31:44.000';
|
||||
var toTime = '2015-09-23 18:31:44.000';
|
||||
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
.then(function () {
|
||||
common.debug('navigateTo');
|
||||
return settingsPage.navigateTo();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('createIndexPattern');
|
||||
return settingsPage.createIndexPattern();
|
||||
})
|
||||
.then(function () {
|
||||
return settingsPage.clickAdvancedTab();
|
||||
})
|
||||
.then(function GetAdvancedSetting() {
|
||||
common.debug('check for required UTC timezone');
|
||||
return settingsPage.getAdvancedSettings('dateFormat:tz');
|
||||
})
|
||||
.then(function (advancedSetting) {
|
||||
expect(advancedSetting).to.be('UTC');
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('navigateToApp visualize');
|
||||
return common.navigateToApp('visualize');
|
||||
})
|
||||
common.debug('navigateToApp visualize');
|
||||
return common.navigateToApp('visualize')
|
||||
.then(function () {
|
||||
common.debug('clickPieChart');
|
||||
return visualizePage.clickPieChart();
|
||||
|
|
|
@ -17,29 +17,8 @@ import {
|
|||
|
||||
bdd.before(function () {
|
||||
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
.then(function () {
|
||||
common.debug('navigateTo');
|
||||
return settingsPage.navigateTo();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('createIndexPattern');
|
||||
return settingsPage.createIndexPattern();
|
||||
})
|
||||
.then(function () {
|
||||
return settingsPage.clickAdvancedTab();
|
||||
})
|
||||
.then(function GetAdvancedSetting() {
|
||||
common.debug('check for required UTC timezone');
|
||||
return settingsPage.getAdvancedSettings('dateFormat:tz');
|
||||
})
|
||||
.then(function (advancedSetting) {
|
||||
expect(advancedSetting).to.be('UTC');
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('navigateToApp visualize');
|
||||
return common.navigateToApp('visualize');
|
||||
})
|
||||
common.debug('navigateToApp visualize');
|
||||
return common.navigateToApp('visualize')
|
||||
.then(function () {
|
||||
common.debug('clickTileMap');
|
||||
return visualizePage.clickTileMap();
|
||||
|
|
|
@ -16,29 +16,8 @@ import {
|
|||
var toTime = '2015-09-23 18:31:44.000';
|
||||
|
||||
bdd.before(function () {
|
||||
return scenarioManager.reload('emptyKibana')
|
||||
.then(function () {
|
||||
common.debug('navigateTo');
|
||||
return settingsPage.navigateTo();
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('createIndexPattern');
|
||||
return settingsPage.createIndexPattern();
|
||||
})
|
||||
.then(function () {
|
||||
return settingsPage.clickAdvancedTab();
|
||||
})
|
||||
.then(function GetAdvancedSetting() {
|
||||
common.debug('check for required UTC timezone');
|
||||
return settingsPage.getAdvancedSettings('dateFormat:tz');
|
||||
})
|
||||
.then(function (advancedSetting) {
|
||||
expect(advancedSetting).to.be('UTC');
|
||||
})
|
||||
.then(function () {
|
||||
common.debug('navigateToApp visualize');
|
||||
return common.navigateToApp('visualize');
|
||||
})
|
||||
common.debug('navigateToApp visualize');
|
||||
return common.navigateToApp('visualize')
|
||||
.then(function () {
|
||||
common.debug('clickVerticalBarChart');
|
||||
return visualizePage.clickVerticalBarChart();
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
import { bdd, remote, common, defaultTimeout, scenarioManager } from '../../../support';
|
||||
import {
|
||||
bdd,
|
||||
remote,
|
||||
common,
|
||||
defaultTimeout,
|
||||
scenarioManager,
|
||||
esClient,
|
||||
elasticDump
|
||||
} from '../../../support';
|
||||
|
||||
(function () {
|
||||
bdd.describe('visualize app', function () {
|
||||
|
@ -7,14 +15,20 @@ import { bdd, remote, common, defaultTimeout, scenarioManager } from '../../../s
|
|||
bdd.before(function () {
|
||||
var self = this;
|
||||
remote.setWindowSize(1200,800);
|
||||
// load a set of makelogs data
|
||||
common.debug('loadIfEmpty logstashFunctional ' + self.timeout);
|
||||
return scenarioManager.loadIfEmpty('logstashFunctional');
|
||||
});
|
||||
|
||||
|
||||
bdd.after(function unloadMakelogs() {
|
||||
return scenarioManager.unload('logstashFunctional');
|
||||
common.debug('Starting visualize before method');
|
||||
var logstash = scenarioManager.loadIfEmpty('logstashFunctional');
|
||||
// delete .kibana index and update configDoc
|
||||
return esClient.deleteAndUpdateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'})
|
||||
.then(function loadkibanaIndexPattern() {
|
||||
common.debug('load kibana index with default index pattern');
|
||||
return elasticDump.elasticLoad('visualize','.kibana');
|
||||
})
|
||||
// wait for the logstash data load to finish if it hasn't already
|
||||
.then(function () {
|
||||
return logstash;
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
require('./_chart_types');
|
||||
|
|
0
test/screenshots/baseline/.empty
Normal file
BIN
test/screenshots/baseline/screenshot-AreaChart.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
test/screenshots/baseline/screenshot-Dashboard Test 1.png
Normal file
After Width: | Height: | Size: 162 KiB |
BIN
test/screenshots/baseline/screenshot-DataTable.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
test/screenshots/baseline/screenshot-LineChart.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
test/screenshots/baseline/screenshot-PieChart.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
test/screenshots/baseline/screenshot-TileMap.png
Normal file
After Width: | Height: | Size: 675 KiB |
BIN
test/screenshots/baseline/screenshot-VerticalBarChart.png
Normal file
After Width: | Height: | Size: 48 KiB |
|
@ -42,16 +42,12 @@ export default (function () {
|
|||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Add fields to the config doc (like setting timezone and defaultIndex)
|
||||
* @return {Promise} A promise that is resolved when elasticsearch has a response
|
||||
/*
|
||||
** Gets configId which is needed when we're going to update the config doc.
|
||||
** Also used after deleting .kibana index to know Kibana has recreated it.
|
||||
*/
|
||||
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;
|
||||
getConfigId: function () {
|
||||
var configId;
|
||||
var docMapString = JSON.stringify(docMap);
|
||||
|
||||
return this.client.search({
|
||||
index: '.kibana',
|
||||
|
@ -70,11 +66,25 @@ export default (function () {
|
|||
} else {
|
||||
configId = response.hits.hits[0]._id;
|
||||
common.debug('config._id =' + configId);
|
||||
return configId;
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 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.getConfigId()
|
||||
// now that we have the id, we can update
|
||||
// return scenarioManager.updateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'});
|
||||
.then(function (response) {
|
||||
.then(function (configId) {
|
||||
common.debug('updating config with ' + docMapString);
|
||||
return self.client.update({
|
||||
index: '.kibana',
|
||||
|
@ -89,7 +99,35 @@ export default (function () {
|
|||
.catch(function (err) {
|
||||
throw err;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Wrap the common 'delete index', 'updateConfigDoc' into one.
|
||||
* [docMap] is optional.
|
||||
* @return {Promise} A promise that is resolved when elasticsearch has a response
|
||||
*/
|
||||
deleteAndUpdateConfigDoc: function (docMap) {
|
||||
var self = this;
|
||||
var configId;
|
||||
|
||||
return this.delete('.kibana')
|
||||
.then(function () {
|
||||
if (!docMap) {
|
||||
return common.try(function () {
|
||||
return self.getConfigId();
|
||||
});
|
||||
} else {
|
||||
var docMapString = JSON.stringify(docMap);
|
||||
return common.try(function () {
|
||||
return self.updateConfigDoc(docMap);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(function (err) {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return EsClient;
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
import { config, defaultTryTimeout, defaultFindTimeout, remote, shieldPage } from '../';
|
||||
import fs from 'fs';
|
||||
import mkdirp from 'mkdirp';
|
||||
import { promisify } from 'bluebird';
|
||||
|
||||
const mkdirpAsync = promisify(mkdirp);
|
||||
const writeFileAsync = promisify(fs.writeFile);
|
||||
|
||||
export default (function () {
|
||||
var Promise = require('bluebird');
|
||||
var moment = require('moment');
|
||||
var testSubjSelector = require('@spalger/test-subj-selector');
|
||||
var getUrl = require('../../utils/get_url');
|
||||
var fs = require('fs');
|
||||
var _ = require('lodash');
|
||||
var parse = require('url').parse;
|
||||
var format = require('url').format;
|
||||
|
@ -244,34 +249,32 @@ export default (function () {
|
|||
.then(function () { self.debug('... sleep(' + sleepMilliseconds + ') end'); });
|
||||
},
|
||||
|
||||
handleError: function (testObj) {
|
||||
var self = this;
|
||||
var testName = (testObj.parent) ? [testObj.parent.name, testObj.name].join('_') : testObj.name;
|
||||
handleError(testObj) {
|
||||
const testName = (testObj.parent) ? [testObj.parent.name, testObj.name].join('_') : testObj.name;
|
||||
return reason => {
|
||||
const now = Date.now();
|
||||
const fileName = `failure_${now}_${testName}.png`;
|
||||
|
||||
return function (reason) {
|
||||
var now = Date.now();
|
||||
var filename = ['failure', now, testName].join('_') + '.png';
|
||||
|
||||
return self.saveScreenshot(filename)
|
||||
return this.saveScreenshot(fileName, true)
|
||||
.finally(function () {
|
||||
throw reason;
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
saveScreenshot: function saveScreenshot(filename) {
|
||||
var self = this;
|
||||
var outDir = path.resolve('test', 'output');
|
||||
async saveScreenshot(fileName, isFailure = false) {
|
||||
try {
|
||||
const directoryName = isFailure ? 'failure' : 'session';
|
||||
const directoryPath = path.resolve(`test/screenshots/${directoryName}`);
|
||||
const filePath = path.resolve(directoryPath, fileName);
|
||||
this.debug(`Taking screenshot "${filePath}"`);
|
||||
|
||||
return self.remote.takeScreenshot()
|
||||
.then(function writeScreenshot(data) {
|
||||
var filepath = path.resolve(outDir, filename);
|
||||
self.debug('Taking screenshot "' + filepath + '"');
|
||||
fs.writeFileSync(filepath, data);
|
||||
})
|
||||
.catch(function (err) {
|
||||
self.log('SCREENSHOT FAILED: ' + err);
|
||||
});
|
||||
const screenshot = await this.remote.takeScreenshot();
|
||||
await mkdirpAsync(directoryPath);
|
||||
await writeFileAsync(filePath, screenshot);
|
||||
} catch (err) {
|
||||
this.log(`SCREENSHOT FAILED: ${err}`);
|
||||
}
|
||||
},
|
||||
|
||||
findTestSubject: function findTestSubject(selector) {
|
||||
|
|
|
@ -225,6 +225,24 @@ export default (function () {
|
|||
return thisTime
|
||||
.findByClassName('sidebar-list')
|
||||
.getProperty('clientWidth');
|
||||
},
|
||||
|
||||
hasNoResults: function hasNoResults() {
|
||||
return common
|
||||
.findTestSubject('discoverNoResults')
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
},
|
||||
|
||||
getNoResultsTimepicker: function getNoResultsTimepicker() {
|
||||
return common.findTestSubject('discoverNoResultsTimefilter');
|
||||
},
|
||||
|
||||
hasNoResultsTimepicker: function hasNoResultsTimepicker() {
|
||||
return this
|
||||
.getNoResultsTimepicker()
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -48,6 +48,13 @@ export default (function () {
|
|||
.findDisplayedByClassName('navbar-timepicker-time-desc').click();
|
||||
},
|
||||
|
||||
isTimepickerOpen: function isTimepickerOpen() {
|
||||
return this.remote.setFindTimeout(defaultFindTimeout)
|
||||
.findDisplayedByCssSelector('.kbn-timepicker')
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
},
|
||||
|
||||
clickAbsoluteButton: function clickAbsoluteButton() {
|
||||
return this.remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByLinkText('Absolute').click();
|
||||
|
|
43
utilities/compareScreenshots.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const imageDiff = require('image-diff');
|
||||
const mkdirp = require('mkdirp');
|
||||
|
||||
function compareScreenshots() {
|
||||
const SCREENSHOTS_DIR = 'test/screenshots';
|
||||
const BASELINE_SCREENSHOTS_DIR = path.resolve(SCREENSHOTS_DIR, 'baseline');
|
||||
const DIFF_SCREENSHOTS_DIR = path.resolve(SCREENSHOTS_DIR, 'diff');
|
||||
const SESSION_SCREENSHOTS_DIR = path.resolve(SCREENSHOTS_DIR, 'session');
|
||||
|
||||
// We don't need to create the baseline dir because it's committed.
|
||||
mkdirp.sync(DIFF_SCREENSHOTS_DIR);
|
||||
mkdirp.sync(SESSION_SCREENSHOTS_DIR);
|
||||
|
||||
fs.readdir(SESSION_SCREENSHOTS_DIR, (readDirError, files) => {
|
||||
const screenshots = files.filter(file => file.indexOf('.png') !== -1);
|
||||
|
||||
screenshots.forEach(screenshot => {
|
||||
const sessionImagePath = path.resolve(SESSION_SCREENSHOTS_DIR, screenshot);
|
||||
const baselineImagePath = path.resolve(BASELINE_SCREENSHOTS_DIR, screenshot);
|
||||
const diffImagePath = path.resolve(DIFF_SCREENSHOTS_DIR, screenshot);
|
||||
|
||||
imageDiff.getFullResult({
|
||||
actualImage: sessionImagePath,
|
||||
expectedImage: baselineImagePath,
|
||||
diffImage: diffImagePath,
|
||||
shadow: true,
|
||||
}, (comparisonError, result) => {
|
||||
if (comparisonError) {
|
||||
throw comparisonError;
|
||||
}
|
||||
|
||||
const change = result.percentage;
|
||||
const changePercentage = (change * 100).toFixed(2);
|
||||
console.log(`${screenshot} has changed by ${changePercentage}%`);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
compareScreenshots();
|