mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Merge branch 'master' into feature/design
This commit is contained in:
commit
cf838fff96
22 changed files with 265 additions and 51 deletions
|
@ -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.
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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());
|
||||
});
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
18
src/ui/__tests__/fixtures/plugin_async_foo/index.js
Normal file
18
src/ui/__tests__/fixtures/plugin_async_foo/index.js
Normal 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 };
|
||||
}
|
||||
}
|
||||
});
|
4
src/ui/__tests__/fixtures/plugin_async_foo/package.json
Normal file
4
src/ui/__tests__/fixtures/plugin_async_foo/package.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "plugin_async_foo",
|
||||
"version": "0.0.0"
|
||||
}
|
14
src/ui/__tests__/fixtures/plugin_bar/index.js
Normal file
14
src/ui/__tests__/fixtures/plugin_bar/index.js
Normal 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 };
|
||||
}
|
||||
}
|
||||
});
|
4
src/ui/__tests__/fixtures/plugin_bar/package.json
Normal file
4
src/ui/__tests__/fixtures/plugin_bar/package.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "plugin_bar",
|
||||
"version": "0.0.0"
|
||||
}
|
14
src/ui/__tests__/fixtures/plugin_foo/index.js
Normal file
14
src/ui/__tests__/fixtures/plugin_foo/index.js
Normal 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 };
|
||||
}
|
||||
}
|
||||
});
|
4
src/ui/__tests__/fixtures/plugin_foo/package.json
Normal file
4
src/ui/__tests__/fixtures/plugin_foo/package.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "plugin_foo",
|
||||
"version": "0.0.0"
|
||||
}
|
|
@ -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'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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, {
|
||||
|
|
|
@ -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!');
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue