Merge branch 'feature/design' of github.com:elastic/kibana into selectable_list_directive

This commit is contained in:
Shelby Sturgis 2016-03-22 09:16:17 -07:00
commit 1dc9a18de7
44 changed files with 422 additions and 139 deletions

View file

@ -128,7 +128,7 @@ Runs both server and browser tests, but skips linting
Run only the server tests
`npm run test:browser`
Run only the browser tests
Run only the browser tests. Coverage reports are available for browser tests by running `npm run test:coverage`. You can find the results under the `coverage/` directory that will be created upon completion.
`npm run test:dev`
Initializes an environment for debugging the browser tests. Includes an dedicated instance of the kibana server for building the test bundle, and a karma server. When running this task the build is optimized for the first time and then a karma-owned instance of the browser is opened. Click the "debug" button to open a new tab that executes the unit tests.

View file

@ -1,6 +1,8 @@
[[kibana-settings-reference]]
WARNING: Modifying the following settings can signficantly affect Kibana's performance and cause problems that are difficult to diagnose. Setting a property's value to a blank field will revert to the default behavior, which may not be compatible with other configuration settings. Deleting a custom setting removes it from Kibana permanently.
WARNING: Modifying the following settings can signficantly affect Kibana's performance and cause problems that are
difficult to diagnose. Setting a property's value to a blank field will revert to the default behavior, which may not be
compatible with other configuration settings. Deleting a custom setting removes it from Kibana permanently.
.Kibana Settings Reference
[horizontal]
@ -8,30 +10,43 @@ WARNING: Modifying the following settings can signficantly affect Kibana's perfo
`sort:options`:: Options for the Elasticsearch https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-sort.html[sort] parameter.
`dateFormat`:: The format to use for displaying pretty-formatted dates.
`dateFormat:tz`:: The timezone that Kibana uses. The default value of `Browser` uses the timezone detected by the browser.
`dateFormat:scaled`:: These values define the format used to render ordered time-based data. Formatted timestamps must adapt to the interval between measurements. Keys are http://en.wikipedia.org/wiki/ISO_8601#Time_intervals[ISO8601 intervals].
`dateFormat:scaled`:: These values define the format used to render ordered time-based data. Formatted timestamps must
adapt to the interval between measurements. Keys are http://en.wikipedia.org/wiki/ISO_8601#Time_intervals[ISO8601 intervals].
`defaultIndex`:: Default is `null`. This property specifies the default index.
`metaFields`:: An array of fields outside of `_source`. Kibana merges these fields into the document when displaying the document.
`metaFields`:: An array of fields outside of `_source`. Kibana merges these fields into the document when displaying the
document.
`discover:sampleSize`:: The number of rows to show in the Discover table.
`doc_table:highlight`:: Highlight results in Discover and Saved Searches Dashboard. Highlighing makes request slow when working on big documents. Set this property to `false` to disable highlighting.
`courier:maxSegmentCount`:: Kibana splits requests in the Discover app into segments to limit the size of requests sent to the Elasticsearch cluster. This setting constrains the length of the segment list. Long segment lists can significantly increase request processing time.
`doc_table:highlight`:: Highlight results in Discover and Saved Searches Dashboard. Highlighing makes request slow when
working on big documents. Set this property to `false` to disable highlighting.
`courier:maxSegmentCount`:: Kibana splits requests in the Discover app into segments to limit the size of requests sent to
the Elasticsearch cluster. This setting constrains the length of the segment list. Long segment lists can significantly
increase request processing time.
`fields:popularLimit`:: This setting governs how many of the top most popular fields are shown.
`histogram:barTarget`:: When date histograms use the `auto` interval, Kibana attempts to generate this number of bars.
`histogram:maxBars`:: Date histograms are not generated with more bars than the value of this property, scaling values when necessary.
`visualization:tileMap:maxPrecision`:: The maximum geoHash precision displayed on tile maps: 7 is high, 10 is very high, 12 is the maximum. http://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geohashgrid-aggregation.html#_cell_dimensions_at_the_equator[Explanation of cell dimensions].
`histogram:maxBars`:: Date histograms are not generated with more bars than the value of this property, scaling values
when necessary.
`visualization:tileMap:maxPrecision`:: The maximum geoHash precision displayed on tile maps: 7 is high, 10 is very high,
12 is the maximum. http://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geohashgrid-aggregation.html#_cell_dimensions_at_the_equator[Explanation of cell dimensions].
`visualization:tileMap:WMSdefaults`:: Default properties for the WMS map server support in the tile map.
`visualization:colorMapping`:: Maps values to specified colors within visualizations.
`visualization:loadingDelay`:: Time to wait before dimming visualizations during query.
`csv:separator`:: A string that serves as the separator for exported values.
`csv:quoteValues`:: Set this property to `true` to quote exported values.
`history:limit`:: In fields that have history, such as query inputs, the value of this property limits how many recent values are shown.
`shortDots:enable`:: Set this property to `true` to shorten long field names in visualizations. For example, instead of `foo.bar.baz`, show `f.b.baz`.
`truncate:maxHeight`:: This property specifies the maximum height that a cell occupies in a table. A value of 0 disables truncation.
`indexPattern:fieldMapping:lookBack`:: The value of this property sets the number of recent matching patterns to query the field mapping for index patterns with names that contain timestamps.
`format:defaultTypeMap`:: A map of the default format name for each field type. Field types that are not explicitly mentioned use "_default_".
`history:limit`:: In fields that have history, such as query inputs, the value of this property limits how many recent
values are shown.
`shortDots:enable`:: Set this property to `true` to shorten long field names in visualizations. For example, instead of
`foo.bar.baz`, show `f.b.baz`.
`truncate:maxHeight`:: This property specifies the maximum height that a cell occupies in a table. A value of 0 disables
truncation.
`indexPattern:fieldMapping:lookBack`:: The value of this property sets the number of recent matching patterns to query the
field mapping for index patterns with names that contain timestamps.
`format:defaultTypeMap`:: A map of the default format name for each field type. Field types that are not explicitly
mentioned use "_default_".
`format:number:defaultPattern`:: Default numeral format for the "number" format.
`format:bytes:defaultPattern`:: Default numeral format for the "bytes" format.
`format:percent:defaultPattern`:: Default numeral format for the "percent" format.
`format:currency:defaultPattern`:: Default numeral format for the "currency" format.
`savedObjects:perPage`:: The number of objects shown on each page of the list of saved objects. The default value is 5.
`timepicker:timeDefaults`:: The default time filter selection.
`timepicker:refreshIntervalDefaults`:: The time filter's default refresh interval.
`dashboard:defaultDarkTheme`:: Set this property to `true` to make new dashboards use the dark theme by default.
`dashboard:defaultDarkTheme`:: Set this property to `true` to make new dashboards use the dark theme by default.

View file

@ -45,6 +45,8 @@ sub-aggregation from the list of types.
You can use the up or down arrows to the right of the aggregation's type to change the aggregation's priority.
Enter a string in the *Custom Label* field to change the display label.
You can click the *Advanced* link to display more customization options for your metrics or bucket aggregation:
*Exclude Pattern*:: Specify a pattern in this field to exclude from the results.

View file

@ -4,6 +4,7 @@
A metric visualization displays a single number for each aggregation you select:
include::y-axis-aggs.asciidoc[]
You can click the *Advanced* link to display more customization options:
*JSON Input*:: A text field where you can add specific JSON-formatted properties to merge with the aggregation
@ -17,7 +18,7 @@ NOTE: In Elasticsearch releases 1.4.3 and later, this functionality requires you
The availability of these options varies depending on the aggregation you choose.
Click the *Options* tab to change the font used to display the metrics.
Click the *Options* tab to display the font size slider.
[float]
[[metric-viewing-detailed-information]]

View file

@ -11,6 +11,8 @@ field. Select a field from the drop-down.
*Unique Count*:: The {ref}search-aggregations-metrics-cardinality-aggregation.html[_cardinality_] aggregation returns
the number of unique values in a field. Select a field from the drop-down.
Enter a string in the *Custom Label* field to change the display label.
The _buckets_ aggregations determine what information is being retrieved from your data set.
Before you choose a buckets aggregation, specify if you are splitting slices within a single chart or splitting into
@ -57,6 +59,8 @@ aggregation's type to change the aggregation's priority.
include::color-picker.asciidoc[]
Enter a string in the *Custom Label* field to change the display label.
You can click the *Advanced* link to display more customization options for your metrics or bucket aggregation:
*Exclude Pattern*:: Specify a pattern in this field to exclude from the results.

View file

@ -65,6 +65,11 @@ bin/kibana plugin --remove marvel
You can also remove a plugin manually by deleting the plugin's subdirectory under the `installedPlugins` directory.
[float]
=== Listing Installed Plugins
Use `--list` or `-l` option to list the currently installed plugins.
[float]
=== Updating Plugins

View file

@ -19,6 +19,8 @@ numeric field. Select a field from the drop-down.
*Unique Count*:: The {ref}search-aggregations-metrics-cardinality-aggregation.html[_cardinality_] aggregation returns
the number of unique values in a field. Select a field from the drop-down.
Enter a string in the *Custom Label* field to change the display label.
The _buckets_ aggregations determine what information is being retrieved from your data set.
Before you choose a buckets aggregation, specify if you are splitting the chart or displaying the buckets as *Geo
@ -71,6 +73,8 @@ based on the geohash coordinates.
NOTE: By default, the *Change precision on map zoom* box is checked. Uncheck the box to disable this behavior.
Enter a string in the *Custom Label* field to change the display label.
You can click the *Advanced* link to display more customization options for your metrics or bucket aggregation:
*Exclude Pattern*:: Specify a pattern in this field to exclude from the results.

View file

@ -26,6 +26,8 @@ values field. Click *+Add* to add a values field.
You can add an aggregation by clicking the *+ Add Aggregation* button.
Enter a string in the *Custom Label* field to change the display label.
The _buckets_ aggregations determine what information is being retrieved from your data set.
Before you choose a buckets aggregation, specify if you are splitting slices within a single chart or splitting into
@ -36,6 +38,8 @@ include::x-axis-aggs.asciidoc[]
include::color-picker.asciidoc[]
Enter a string in the *Custom Label* field to change the display label.
You can click the *Advanced* link to display more customization options for your metrics or bucket aggregation:
*Exclude Pattern*:: Specify a pattern in this field to exclude from the results.

View file

@ -28,8 +28,8 @@ remove a range.
or bottom _n_ elements of a given field to display, ordered by count or a custom metric.
*Filters*:: You can specify a set of {ref}/search-aggregations-bucket-filters-aggregation.html[_filters_] for the data.
You can specify a filter as a query string or in JSON format, just as in the Discover search bar. Click *Add Filter* to
add another filter. Click the image:images/labelbutton.png[] *label* button to open the label field, where you can type in a
name to display on the visualization.
add another filter. Click the image:images/labelbutton.png[Label button icon] *label* button to open the label field, where
you can type in a name to display on the visualization.
*Significant Terms*:: Displays the results of the experimental
{ref}/search-aggregations-bucket-significantterms-aggregation.html[_significant terms_] aggregation.
@ -39,3 +39,5 @@ from the list of types.
When multiple aggregations are defined on a chart's axis, you can use the up or down arrows to the right of the
aggregation's type to change the aggregation's priority.
Enter a string in the *Custom Label* field to change the display label.

View file

@ -22,3 +22,5 @@ from the drop-down, then specify one or more percentile rank values in the *Valu
values field. Click *+Add* to add a values field.
You can add an aggregation by clicking the *+ Add Aggregation* button.
Enter a string in the *Custom Label* field to change the display label.

View file

@ -156,7 +156,7 @@
"grunt-cli": "0.1.13",
"grunt-contrib-clean": "0.6.0",
"grunt-contrib-copy": "0.8.1",
"grunt-esvm": "3.0.3",
"grunt-esvm": "3.0.4",
"grunt-karma": "0.12.0",
"grunt-run": "0.5.0",
"grunt-s3": "0.2.0-alpha.3",

View file

@ -33,6 +33,16 @@ module.exports = function ({ Plugin }) {
}).default();
},
uiExports: {
injectDefaultVars(server, options) {
return {
esRequestTimeout: options.requestTimeout,
esShardTimeout: options.shardTimeout,
esApiVersion: options.apiVersion,
};
}
},
init(server, options) {
const kibanaIndex = server.config().get('kibana.index');

View file

@ -1,9 +1,8 @@
import expect from 'expect.js';
import util from 'util';
import { format } from 'util';
import * as kbnTestServer from '../../../../../test/utils/kbn_server';
const format = util.format;
import fromRoot from '../../../../utils/from_root';
describe('plugins/elasticsearch', function () {
describe('routes', function () {
@ -13,7 +12,13 @@ describe('plugins/elasticsearch', function () {
before(function () {
this.timeout(60000); // sometimes waiting for server takes longer than 10
kbnServer = kbnTestServer.createServer();
kbnServer = kbnTestServer.createServer({
plugins: {
scanDirs: [
fromRoot('src/plugins')
]
}
});
return kbnServer.ready()
.then(() => kbnServer.server.plugins.elasticsearch.waitUntilReady());
});

View file

@ -67,7 +67,12 @@ module.exports = function (kibana) {
description: 'define index patterns, change config, and more',
icon: 'plugins/kibana/assets/settings.svg',
}
]
],
injectDefaultVars(server, options) {
return {
kbnIndex: options.index
};
},
},
init: function (server, options) {

View file

@ -156,21 +156,3 @@ dashboard-grid {
.dashboard-panel-picker > .list-group-item {
border-top: 0;
}
.dashboard-load {
margin: 10px;
}
.dashboard {
&-info {
line-height: 30px;
padding: 0px 10px;
border-bottom-left-radius: @border-radius-base;
&-title {
font-weight: bold;
margin-right: 10px;
}
}
}

View file

@ -39,21 +39,21 @@ describe('createMappingsFromPatternFields', function () {
let mappings = createMappingsFromPatternFields(testFields);
_.forEach(mappings, function (mapping) {
if (mapping.type !== 'string') {
if (mapping.type !== 'text') {
expect(_.isEqual(mapping, {
type: mapping.type,
index: 'not_analyzed',
index: true,
doc_values: true
})).to.be.ok();
}
});
});
it('should give strings a multi-field mapping', function () {
it('should give strings a multi-field mapping with a "text" base type', function () {
let mappings = createMappingsFromPatternFields(testFields);
_.forEach(mappings, function (mapping) {
if (mapping.type === 'string') {
if (mapping.type === 'text') {
expect(mapping).to.have.property('fields');
}
});
@ -68,7 +68,7 @@ describe('createMappingsFromPatternFields', function () {
expect(mappings.geo.properties).to.have.property('coordinates');
expect(_.isEqual(mappings.geo.properties.coordinates, {
type: 'geo_point',
index: 'not_analyzed',
index: true,
doc_values: true
})).to.be.ok();
});

View file

@ -13,10 +13,9 @@ module.exports = function createMappingsFromPatternFields(fields) {
if (field.type === 'string') {
mapping = {
type: 'string',
index: 'analyzed',
type: 'text',
fields: {
raw: {type: 'string', index: 'not_analyzed', doc_values: true, ignore_above: 256}
raw: {type: 'keyword', ignore_above: 256}
}
};
}
@ -24,7 +23,7 @@ module.exports = function createMappingsFromPatternFields(fields) {
const fieldType = field.type === 'number' ? 'double' : field.type;
mapping = {
type: fieldType,
index: 'not_analyzed',
index: true,
doc_values: true
};
}

View file

@ -58,10 +58,9 @@ module.exports = function registerPost(server) {
match: '*',
match_mapping_type: 'string',
mapping: {
type: 'string',
index: 'analyzed',
type: 'text',
fields: {
raw: {type: 'string', index: 'not_analyzed', doc_values: true, ignore_above: 256}
raw: {type: 'keyword', ignore_above: 256}
}
}
}

View file

@ -1,9 +1,11 @@
import _ from 'lodash';
import Joi from 'joi';
import { attempt, fromNode } from 'bluebird';
import Bluebird, { attempt, fromNode } from 'bluebird';
import { basename, resolve } from 'path';
import { inherits } from 'util';
const extendInitFns = Symbol('extend plugin initialization');
const defaultConfigSchema = Joi.object({
enabled: Joi.boolean().default(true)
}).default();
@ -57,6 +59,7 @@ module.exports = class Plugin {
this.externalInit = opts.init || _.noop;
this.getConfigSchema = opts.config || _.noop;
this.init = _.once(this.init);
this[extendInitFns] = [];
if (opts.publicDir === false) {
this.publicDir = null;
@ -98,14 +101,12 @@ module.exports = class Plugin {
let { config } = kbnServer;
// setup the hapi register function and get on with it
let register = (server, options, next) => {
const asyncRegister = async (server, options) => {
this.server = server;
// bind the server and options to all
// apps created by this plugin
for (let app of this.apps) {
app.getInjectedVars = _.partial(app.getInjectedVars, server, options);
}
await Promise.all(this[extendInitFns].map(async fn => {
await fn.call(this, server, options);
}));
server.log(['plugins', 'debug'], {
tmpl: 'Initializing plugin <%= plugin.id %>',
@ -119,7 +120,11 @@ module.exports = class Plugin {
this.status = kbnServer.status.create(`plugin:${this.id}`);
server.expose('status', this.status);
attempt(this.externalInit, [server, options], this).nodeify(next);
return await attempt(this.externalInit, [server, options], this);
};
const register = (server, options, next) => {
Bluebird.resolve(asyncRegister(server, options)).nodeify(next);
};
register.attributes = { name: id, version: version };
@ -138,6 +143,10 @@ module.exports = class Plugin {
}
}
extendInit(fn) {
this[extendInitFns].push(fn);
}
toJSON() {
return this.pkg;
}

View file

@ -0,0 +1,18 @@
import Bluebird from 'bluebird';
export default kibana => new kibana.Plugin({
config(Joi) {
return Joi.object().keys({
enabled: Joi.boolean().default(true),
delay: Joi.number().required(),
shared: Joi.string(),
}).default();
},
uiExports: {
async injectDefaultVars(server, options) {
await Bluebird.delay(options.delay);
return { shared: options.shared };
}
}
});

View file

@ -0,0 +1,4 @@
{
"name": "plugin_async_foo",
"version": "0.0.0"
}

View file

@ -0,0 +1,14 @@
export default kibana => new kibana.Plugin({
config(Joi) {
return Joi.object().keys({
enabled: Joi.boolean().default(true),
shared: Joi.string()
}).default();
},
uiExports: {
injectDefaultVars(server, options) {
return { shared: options.shared };
}
}
});

View file

@ -0,0 +1,4 @@
{
"name": "plugin_bar",
"version": "0.0.0"
}

View file

@ -0,0 +1,14 @@
export default kibana => new kibana.Plugin({
config(Joi) {
return Joi.object().keys({
enabled: Joi.boolean().default(true),
shared: Joi.string()
}).default();
},
uiExports: {
injectDefaultVars(server, options) {
return { shared: options.shared };
}
}
});

View file

@ -0,0 +1,4 @@
{
"name": "plugin_foo",
"version": "0.0.0"
}

View file

@ -1,6 +1,8 @@
import expect from 'expect.js';
import { resolve } from 'path';
import UiExports from '../ui_exports';
import * as kbnTestServer from '../../../test/utils/kbn_server';
describe('UiExports', function () {
describe('#find()', function () {
@ -23,4 +25,83 @@ describe('UiExports', function () {
expect(uiExports.find(['foo', 'bar'])).to.eql(['a', 'b', 'c']);
});
});
//
describe('#defaultInjectedVars', function () {
context('two plugins, two sync', function () {
this.slow(10000);
this.timeout(60000);
let kbnServer;
before(async function () {
kbnServer = kbnTestServer.createServer({
plugins: {
paths: [
resolve(__dirname, 'fixtures/plugin_bar'),
resolve(__dirname, 'fixtures/plugin_foo')
]
},
plugin_foo: {
shared: 'foo'
},
plugin_bar: {
shared: 'bar'
}
});
await kbnServer.ready();
});
after(async function () {
await kbnServer.close();
});
it('merges the two plugins in the order they are loaded', function () {
expect(kbnServer.uiExports.defaultInjectedVars).to.eql({
shared: 'foo'
});
});
});
context('two plugins, one async', function () {
this.slow(10000);
this.timeout(60000);
let kbnServer;
before(async function () {
kbnServer = kbnTestServer.createServer({
plugins: {
scanDirs: [],
paths: [
resolve(__dirname, 'fixtures/plugin_async_foo'),
resolve(__dirname, 'fixtures/plugin_foo')
]
},
plugin_async_foo: {
delay: 500,
shared: 'foo'
},
plugin_bar: {
shared: 'bar'
}
});
await kbnServer.ready();
});
after(async function () {
await kbnServer.close();
});
it('merges the two plugins in the order they are loaded', function () {
// even though plugin_async_foo loads 500ms later, it is still "first" to merge
expect(kbnServer.uiExports.defaultInjectedVars).to.eql({
shared: 'foo'
});
});
});
});
});

View file

@ -59,16 +59,6 @@ module.exports = async (kbnServer, server, config) => {
}
});
const defaultInjectedVars = {};
if (config.has('kibana')) {
defaultInjectedVars.kbnIndex = config.get('kibana.index');
}
if (config.has('elasticsearch')) {
defaultInjectedVars.esRequestTimeout = config.get('elasticsearch.requestTimeout');
defaultInjectedVars.esShardTimeout = config.get('elasticsearch.shardTimeout');
defaultInjectedVars.esApiVersion = config.get('elasticsearch.apiVersion');
}
server.decorate('reply', 'renderApp', function (app) {
const payload = {
app: app,
@ -77,7 +67,7 @@ module.exports = async (kbnServer, server, config) => {
buildNum: config.get('pkg.buildNum'),
buildSha: config.get('pkg.buildSha'),
basePath: config.get('server.basePath'),
vars: defaults(app.getInjectedVars() || {}, defaultInjectedVars),
vars: defaults(app.getInjectedVars() || {}, uiExports.defaultInjectedVars),
};
return this.view(app.templateName, {

View file

@ -37,6 +37,11 @@ export default function AggTypeMetricPercentileRanksProvider(Private) {
name: 'values',
editor: valuesEditor,
default: []
},
{
write(agg, output) {
output.params.keyed = false;
}
}
],
getResponseAggs: function (agg) {

View file

@ -35,6 +35,11 @@ export default function AggTypeMetricPercentilesProvider(Private) {
name: 'percents',
editor: percentsEditor,
default: [1, 5, 25, 50, 75, 95, 99]
},
{
write(agg, output) {
output.params.keyed = false;
}
}
],
getResponseAggs: function (agg) {

View file

@ -85,8 +85,8 @@ describe('FieldFormat class', function () {
var text = f.getConverterFor('text');
var html = f.getConverterFor('html');
expect(text).to.not.be(html);
expect(text()).to.be('formatted');
expect(html()).to.be('formatted');
expect(text('formatted')).to.be('formatted');
expect(html('formatted')).to.be('formatted');
});
it('can be an object, with seperate text and html converter', function () {
@ -99,8 +99,8 @@ describe('FieldFormat class', function () {
var text = f.getConverterFor('text');
var html = f.getConverterFor('html');
expect(text).to.not.be(html);
expect(text()).to.be('formatted text');
expect(html()).to.be('formatted html');
expect(text('formatted text')).to.be('formatted text');
expect(html('formatted html')).to.be('formatted html');
});
it('does not escape the output of the text converter', function () {
@ -147,6 +147,11 @@ describe('FieldFormat class', function () {
var f = new TestFormat();
expect(f.convert('val', 'html')).to.be('html');
});
it('formats a value as " - " when no value is specified', function () {
var f = new TestFormat();
expect(f.convert()).to.be(' - ');
});
});
});

View file

@ -6,6 +6,10 @@ export default function contentTypesProvider(highlightFilter) {
var types = {
html: function (format, convert) {
return function recurse(value, field, hit) {
if (value == null) {
return _.asPrettyString(value);
}
if (!value || typeof value.map !== 'function') {
return convert.call(format, value, field, hit);
}

View file

@ -17,4 +17,14 @@ describe('String Format', function () {
expect(string.convert('Zm9vYmFy')).to.be('foobar');
});
it('convert a string to title case', function () {
var StringFormat = fieldFormats.getType('string');
var string = new StringFormat({
transform: 'title'
});
expect(string.convert('PLEASE DO NOT SHOUT')).to.be('Please Do Not Shout');
expect(string.convert('Mean, variance and standard_deviation.')).to.be('Mean, Variance And Standard_deviation.');
expect(string.convert('Stay CALM!')).to.be('Stay Calm!');
});
});

View file

@ -36,12 +36,14 @@ export default function StringFormatProvider(Private) {
{ id: false, name: '- none -' },
{ id: 'lower', name: 'Lower Case' },
{ id: 'upper', name: 'Upper Case' },
{ id: 'title', name: 'Title Case' },
{ id: 'short', name: 'Short Dots' },
{ id: 'base64', name: 'Base64 Decode'}
];
_String.sampleInputs = [
'A Quick Brown Fox.',
'STAY CALM!',
'com.organizations.project.ClassName',
'hostname.net',
'SGVsbG8gd29ybGQ='
@ -55,10 +57,15 @@ export default function StringFormatProvider(Private) {
}
};
_String.prototype._toTitleCase = function (val) {
return val.replace(/\w\S*/g, txt => { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); });
};
_String.prototype._convert = function (val) {
switch (this.param('transform')) {
case 'lower': return String(val).toLowerCase();
case 'upper': return String(val).toUpperCase();
case 'title': return this._toTitleCase(val);
case 'short': return _.shortenDottedString(val);
case 'base64': return this._base64Decode(val);
default: return _.asPrettyString(val);

View file

@ -142,8 +142,14 @@ a {
.kibana-nav-options {
padding-bottom: 0;
padding-right: 0px;
.kibana-nav-actions {
margin-left: auto;
.button-group > :last-child {
border-radius: 0;
}
}
.kibana-nav-info {
line-height: 30px;

View file

@ -1,32 +1,12 @@
<ul ng-show="timefilter.enabled" class="nav navbar-nav navbar-right navbar-timepicker">
<li>
<button
ng-click="toggleRefresh()"
ng-show="timefilter.refreshInterval.value > 0">
<i class="fa" ng-class="timefilter.refreshInterval.pause ? 'fa-play' : 'fa-pause'"></i>
</button>
</li>
<li
ng-show="timefilter.refreshInterval.value > 0 || configTemplate.is('interval') || configTemplate.is('filter')">
<button ng-click="configTemplate.toggle('interval')"
ng-class="{active: configTemplate.is('interval') }"
class="navbar-timepicker-auto-refresh-desc">
<span ng-show="timefilter.refreshInterval.value === 0"><i class="fa fa-repeat"></i> Auto-refresh</span>
<span ng-show="timefilter.refreshInterval.value > 0">{{timefilter.refreshInterval.display}}</span>
</button>
</li>
<li>
<button ng-class="{active: configTemplate.is('filter')}"
ng-click="configTemplate.toggle('filter')"
aria-haspopup="true"
aria-expanded="false"
class="navbar-timepicker-time-desc">
<i aria-hidden="true" class="fa fa-clock-o"></i>
<pretty-duration from="timefilter.time.from" to="timefilter.time.to"></pretty-duration>
</button>
</li>
</ul>
<div ng-show="timefilter.enabled" class="button-group">
<button ng-click="toggleRefresh()" ng-show="timefilter.refreshInterval.value > 0">
<i class="fa fa-pause" ng-class="timefilter.refreshInterval.pause ? 'fa-play' : 'fa-pause'"></i>
</button>
<button ng-click="configTemplate.toggle('interval')" ng-class="{active: configTemplate.is('interval') }" class="navbar-timepicker-auto-refresh-desc" ng-show="timefilter.refreshInterval.value > 0 || configTemplate.is('interval') || configTemplate.is('filter')">
<span ng-show="timefilter.refreshInterval.value === 0"><i class="fa fa-repeat"></i> Auto-refresh</span>
<span ng-show="timefilter.refreshInterval.value > 0">{{timefilter.refreshInterval.display}}</span>
</button><button ng-class="{active: configTemplate.is('filter')}" ng-click="configTemplate.toggle('filter')" aria-haspopup="true" aria-expanded="false" class="navbar-timepicker-time-desc">
<i aria-hidden="true" class="fa fa-clock-o"></i>
<pretty-duration from="timefilter.time.from" to="timefilter.time.to"></pretty-duration>
</button>
</div>

View file

@ -13,6 +13,7 @@ class UiExports {
this.exportConsumer = _.memoize(this.exportConsumer);
this.consumers = [];
this.bundleProviders = [];
this.defaultInjectedVars = {};
}
consumePlugin(plugin) {
@ -52,7 +53,17 @@ class UiExports {
return (plugin, specs) => {
const id = plugin.id;
for (let spec of [].concat(specs || [])) {
const app = this.apps.new({ id, ...spec });
let app = this.apps.new(_.defaults({}, spec, {
id: plugin.id,
urlBasePath: this.urlBasePath
}));
plugin.extendInit((server, options) => { // eslint-disable-line no-loop-func
const wrapped = app.getInjectedVars;
app.getInjectedVars = () => wrapped.call(plugin, server, options);
});
plugin.apps.add(app);
}
};
@ -88,6 +99,13 @@ class UiExports {
this.aliases[adhocType] = _.union(this.aliases[adhocType] || [], spec);
});
};
case 'injectDefaultVars':
return (plugin, injector) => {
plugin.extendInit(async (server, options) => {
_.merge(this.defaultInjectedVars, await injector.call(plugin, server, options));
});
};
}
}

View file

@ -1,14 +1,18 @@
var path = require('path');
import { resolve as resolveUrl } from 'url';
const URL = 'https://selenium-release.storage.googleapis.com/2.48/selenium-server-standalone-2.48.2.jar';
const DIR = resolveUrl(URL, './');
const FILE = URL.replace(DIR, '');
module.exports = function (grunt) {
return {
options: {
selenium: {
filename: 'selenium-server-standalone-2.48.2.jar',
server: 'https://selenium-release.storage.googleapis.com/2.48/',
filename: FILE,
server: DIR,
md5: 'b2784fc67c149d3c13c83d2108104689',
directory: path.join(grunt.config.get('root'), 'selenium')
path: path.join(grunt.config.get('root'), 'selenium', FILE)
}
}
};

View file

@ -122,7 +122,7 @@ module.exports = function (grunt) {
cmd: 'java',
args: [
'-jar',
'selenium/selenium-server-standalone-2.48.2.jar',
'<%= downloadSelenium.options.selenium.path %>',
'-port',
uiConfig.servers.webdriver.port,
]
@ -138,7 +138,7 @@ module.exports = function (grunt) {
cmd: 'java',
args: [
'-jar',
'selenium/selenium-server-standalone-2.48.2.jar',
'<%= downloadSelenium.options.selenium.path %>',
'-port',
uiConfig.servers.webdriver.port,
]

View file

@ -11,8 +11,9 @@ module.exports = function (grunt) {
const done = this.async();
const config = this.options();
const SELENIUM_FILE_PATH = path.join(config.selenium.directory, config.selenium.filename);
const SELENIUM_DOWNLOAD_URL = config.selenium.server + config.selenium.filename;
const FILE = config.selenium.path;
const DIR = path.dirname(config.selenium.path);
const URL = config.selenium.server + config.selenium.filename;
function validateDownload(path, expectedHash, success) {
grunt.log.write('Validating hash...');
@ -28,27 +29,27 @@ module.exports = function (grunt) {
}
function downloadSelenium(success) {
grunt.log.write(`Downloading ${SELENIUM_DOWNLOAD_URL}...`);
request.get(SELENIUM_DOWNLOAD_URL)
.pipe(fs.createWriteStream(SELENIUM_FILE_PATH))
grunt.log.write(`Downloading ${URL}...`);
request.get(URL)
.pipe(fs.createWriteStream(FILE))
.on('error', function downloadError(err) {
grunt.fail.warn(err);
})
.on('finish', function downloadFinish() {
grunt.log.writeln('done');
validateDownload(SELENIUM_FILE_PATH, config.selenium.md5, success);
validateDownload(FILE, config.selenium.md5, success);
});
}
function start() {
try {
fs.mkdirSync(config.selenium.directory);
fs.mkdirSync(DIR);
} catch (err) {
if (err && err.code !== 'EEXIST') grunt.fail.warn(err);
}
if (fs.existsSync(SELENIUM_FILE_PATH)) {
validateDownload(SELENIUM_FILE_PATH, config.selenium.md5, done);
if (fs.existsSync(FILE)) {
validateDownload(FILE, config.selenium.md5, done);
} else {
downloadSelenium(done);
}

View file

@ -1,7 +1,18 @@
import { compact } from 'lodash';
import { delimiter } from 'path';
module.exports = function (grunt) {
let { compact } = require('lodash');
grunt.registerTask('jenkins', 'Jenkins build script', function () {
process.env.JAVA_HOME = '/usr/lib/jvm/jdk8';
// make sure JAVA_HOME points to JDK8
const HOME = '/usr/lib/jvm/jdk8';
process.env.JAVA_HOME = HOME;
// extend PATH to point to JDK8
const path = process.env.PATH.split(delimiter);
path.unshift(`${HOME}/bin`);
process.env.PATH = path.join(delimiter);
// always build os packages on jenkins
grunt.option('os-packages', true);
grunt.task.run(compact([

View file

@ -141,7 +141,7 @@ define(function (require) {
+ ' Here&rsquo;s...", "twitter:card": "summary", "twitter:image": "'
+ 'http://IMAGES1.laweekly.com/imager/the-rapture-at-the-mayan-7-25/u/original/2463272/rapturetn05.jpg",'
+ ' "twitter:site": "@laweekly" } machine.os:win 7 machine.ram:7,516,192,768 _id:AU_x3_g4GFA8no6QjkYX'
+ ' _type:apache _index:logstash-2015.09.22 _score: relatedContent.article:modified_time:November 27th'
+ ' _type:apache _index:logstash-2015.09.22 _score: - relatedContent.article:modified_time:November 27th'
+ ' 2014, 16:00:51.000, November 27th 2014, 16:28:42.000 relatedContent.article:published_time:July 26th'
+ ' 2007, 19:42:30.000, December 13th 2007, 20:19:35.000';
return discoverPage.getDocTableIndex(1)
@ -219,7 +219,7 @@ define(function (require) {
+ ' "Arts", "og:site_name": "LA Weekly", "twitter:title": "Shopping Daze", "twitter:description": "LA'
+ ' Weekly is the definitive source of information for news, music, movies, restaurants, reviews, and'
+ ' events in Los Angeles.", "twitter:card": "summary", "twitter:site": "@laweekly" } machine.os:osx'
+ ' machine.ram:15,032,385,536 _id:AU_x3_g3GFA8no6QjkFm _type:apache _index:logstash-2015.09.20 _score:'
+ ' machine.ram:15,032,385,536 _id:AU_x3_g3GFA8no6QjkFm _type:apache _index:logstash-2015.09.20 _score: -'
+ ' relatedContent.article:modified_time:October 28th 2014, 22:00:08.000, November 26th 2014,'
+ ' 01:05:47.000, November 26th 2014, 03:52:35.000, November 26th 2014, 04:15:21.000, November 27th 2014,'
+ ' 16:01:03.000 relatedContent.article:published_time:October 21st 2005, 01:10:25.000, March 5th 2006,'

View file

@ -326,6 +326,33 @@ define(function (require) {
});
},
clickLoadSavedVisButton: function clickLoadSavedVisButton() {
return this.remote
.setFindTimeout(defaultTimeout)
.findDisplayedByCssSelector('button[aria-label="Load Saved Visualization"]')
.click();
},
filterVisByName: function filterVisByName(vizName) {
return this.remote
.findByCssSelector('input[name="filter"]')
.click()
// can't uses dashes in saved visualizations when filtering
// or extended character sets
// https://github.com/elastic/kibana/issues/6300
.type(vizName.replace('-',' '));
},
clickVisualizationByLinkText: function clickVisualizationByLinkText(vizName) {
var self = this;
common.debug('clickVisualizationByLinkText(' + vizName + ')');
return this.remote
.setFindTimeout(defaultTimeout)
.findByLinkText(vizName)
.click();
},
// this starts by clicking the Load Saved Viz button, not from the
// bottom half of the "Create a new visualization Step 1" page
loadSavedVisualization: function loadSavedVisualization(vizName) {

View file

@ -12,7 +12,12 @@ define(function (require) {
});
bdd.afterEach(function () {
return request.del('/kibana/ingest/logstash-*');
return request.del('/kibana/ingest/logstash-*')
.then(function () {
return scenarioManager.client.indices.delete({
index: 'logstash-*'
});
});
});
bdd.it('should return 400 for an invalid payload', function invalidPayload() {
@ -57,6 +62,29 @@ define(function (require) {
});
});
bdd.it('should successfully create new indices based on the template', function newIndices() {
return request.post('/kibana/ingest')
.send(createTestData())
.expect(204)
.then(function () {
return scenarioManager.client.create({
index: 'logstash-1',
type: 'foo',
id: '1',
body: {
ip: '192.168.1.1',
'@timestamp': '2015-09-20T10:28:22.684Z',
agent: 'Jack',
bytes: 9001,
geo: {coordinates: {lat: 43.07260861, lon: -92.61077833}}
}
})
.then(function (response) {
expect(response.created).to.be.ok();
});
});
});
bdd.it('should provide defaults for field properties', function createTemplate() {
return request.post('/kibana/ingest')
.send(createTestData())
@ -92,15 +120,15 @@ define(function (require) {
.then(function (template) {
var mappings = template['kibana-logstash-*'].mappings._default_.properties;
expect(mappings).to.be.ok();
expect(_.isEqual(mappings.ip, {index: 'not_analyzed', type: 'ip', doc_values: true})).to.be.ok();
expect(_.isEqual(mappings['@timestamp'], {index: 'not_analyzed', type: 'date', doc_values: true})).to.be.ok();
expect(_.isEqual(mappings.bytes, {index: 'not_analyzed', type: 'double', doc_values: true})).to.be.ok();
expect(_.isEqual(mappings.ip, {index: true, type: 'ip', doc_values: true})).to.be.ok();
expect(_.isEqual(mappings['@timestamp'], {index: true, type: 'date', doc_values: true})).to.be.ok();
expect(_.isEqual(mappings.bytes, {index: true, type: 'double', doc_values: true})).to.be.ok();
// object fields are mapped as such, with individual mappings for each of their properties
expect(_.isEqual(mappings.geo, {
properties: {
coordinates: {
index: 'not_analyzed',
index: true,
type: 'geo_point',
doc_values: true
}

View file

@ -3,7 +3,6 @@ import { defaultsDeep, set } from 'lodash';
import { header as basicAuthHeader } from './base_auth';
import { kibanaUser, kibanaServer } from '../shield';
import KbnServer from '../../src/server/kbn_server';
import fromRoot from '../../src/utils/from_root';
import serverConfig from '../server_config';
const SERVER_DEFAULTS = {
@ -16,11 +15,7 @@ const SERVER_DEFAULTS = {
logging: {
quiet: true
},
plugins: {
scanDirs: [
fromRoot('src/plugins')
]
},
plugins: {},
optimize: {
enabled: false
},