Upgrade to eslint 4 (#14862)

* [eslint] upgrade to 4.10.0

* [eslint-config-kibana] limit jest config to jest test files

* [ui_framework] remove trailing comma from rest-spreads

* [dashboard/tests] tag jest helpers with .test.js suffix

* explicitly import expect.js where used

* [eslint] apply auto-fixes

* [eslint] manually add/wrap some parens for compliance

* [npm] point to local packages for testing/review

* [jest] remove .test extension from jest helpers

* [ui_framework] fix trailing comma removal from 3bc661a1c8

* [packages] upgrade eslint packages
This commit is contained in:
Spencer 2017-11-14 18:16:59 -07:00 committed by GitHub
parent 94b2b324aa
commit 5cddc10077
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
380 changed files with 8880 additions and 8855 deletions

View file

@ -214,11 +214,11 @@
"yauzl": "2.7.0"
},
"devDependencies": {
"@elastic/eslint-config-kibana": "0.13.0",
"@elastic/eslint-config-kibana": "0.14.0",
"@elastic/eslint-import-resolver-kibana": "0.9.0",
"@elastic/eslint-plugin-kibana-custom": "1.0.3",
"@elastic/eslint-plugin-kibana-custom": "1.1.0",
"angular-mocks": "1.4.7",
"babel-eslint": "7.2.3",
"babel-eslint": "8.0.2",
"backport": "1.1.1",
"chai": "3.5.0",
"chalk": "2.0.1",
@ -229,13 +229,13 @@
"classnames": "2.2.5",
"enzyme": "2.9.1",
"enzyme-to-json": "1.4.5",
"eslint": "3.19.0",
"eslint-plugin-babel": "4.1.1",
"eslint-plugin-import": "2.3.0",
"eslint-plugin-jest": "21.0.0",
"eslint-plugin-mocha": "4.9.0",
"eslint": "4.10.0",
"eslint-plugin-babel": "4.1.2",
"eslint-plugin-import": "2.8.0",
"eslint-plugin-jest": "21.2.0",
"eslint-plugin-mocha": "4.11.0",
"eslint-plugin-prefer-object-spread": "1.2.1",
"eslint-plugin-react": "7.1.0",
"eslint-plugin-react": "7.4.0",
"event-stream": "3.3.2",
"expect.js": "0.3.1",
"faker": "1.1.0",

View file

@ -1,15 +1,20 @@
module.exports = {
plugins: [
'jest',
],
overrides: [
{
files: ['**/*.test.js'],
plugins: [
'jest',
],
env: {
'jest/globals': true,
},
env: {
'jest/globals': true,
},
rules: {
'jest/no-disabled-tests': 'error',
'jest/no-focused-tests': 'error',
'jest/no-identical-title': 'error',
},
rules: {
'jest/no-disabled-tests': 'error',
'jest/no-focused-tests': 'error',
'jest/no-identical-title': 'error',
},
}
]
};

View file

@ -1,6 +1,6 @@
{
"name": "@elastic/eslint-config-kibana",
"version": "0.13.0",
"version": "0.14.0",
"description": "The eslint config used by the kibana team",
"main": ".eslintrc.js",
"scripts": {
@ -18,7 +18,7 @@
},
"homepage": "https://github.com/elastic/eslint-config-kibana#readme",
"peerDependencies": {
"babel-eslint": "^7.2.3",
"babel-eslint": "^8.0.0",
"eslint": "^4.1.0",
"eslint-plugin-babel": "^4.1.1",
"eslint-plugin-import": "^2.6.0",

View file

@ -1,7 +1,14 @@
module.exports.rules = {
'no-default-export': context => ({
ExportDefaultDeclaration: (node) => {
context.report(node, 'Default exports not allowed.');
module.exports = {
rules: {
'no-default-export': {
meta: {
schema: []
},
create: context => ({
ExportDefaultDeclaration: (node) => {
context.report(node, 'Default exports not allowed.');
}
})
}
})
}
};

View file

@ -1,6 +1,6 @@
{
"name": "@elastic/eslint-plugin-kibana-custom",
"version": "1.0.3",
"version": "1.1.0",
"description": "Custom ESLint rules for Kibana",
"repository": {
"type": "git",

View file

@ -7,29 +7,29 @@ const argv = process.env.kbnWorkerArgv ? JSON.parse(process.env.kbnWorkerArgv) :
const program = new Command('bin/kibana');
program
.version(pkg.version)
.description(
'Kibana is an open source (Apache Licensed), browser ' +
.version(pkg.version)
.description(
'Kibana is an open source (Apache Licensed), browser ' +
'based analytics and search dashboard for Elasticsearch.'
);
);
// attach commands
serveCommand(program);
program
.command('help <command>')
.description('Get the help for a specific command')
.action(function (cmdName) {
const cmd = _.find(program.commands, { _name: cmdName });
if (!cmd) return program.error(`unknown command ${cmdName}`);
cmd.help();
});
.command('help <command>')
.description('Get the help for a specific command')
.action(function (cmdName) {
const cmd = _.find(program.commands, { _name: cmdName });
if (!cmd) return program.error(`unknown command ${cmdName}`);
cmd.help();
});
program
.command('*', null, { noHelp: true })
.action(function (cmd) {
program.error(`unknown command ${cmd}`);
});
.command('*', null, { noHelp: true })
.action(function (cmd) {
program.error(`unknown command ${cmd}`);
});
// check for no command name
const subCommand = argv[2] && !String(argv[2][0]).match(/^-|^\.|\//);

View file

@ -82,8 +82,8 @@ export default class BasePathProxy {
});
}
})
.return(undefined)
.nodeify(reply);
.return(undefined)
.nodeify(reply);
}
],
},

View file

@ -8,7 +8,7 @@ Command.prototype.error = function (err) {
if (err && err.message) err = err.message;
console.log(
`
`
${red(' ERROR ')} ${err}
${help(this, ' ')}
@ -20,7 +20,7 @@ ${help(this, ' ')}
Command.prototype.defaultHelp = function () {
console.log(
`
`
${help(this, ' ')}
`

View file

@ -13,7 +13,7 @@ export default function help(command, spaces) {
const cmdDef = !defCmd ? '' : `=${defCmd._name}`;
return (
`
`
Usage: ${command._name} [command${cmdDef}] [options]
${desc}
@ -58,7 +58,7 @@ function cmdHelp(cmd) {
if (!cmd) return '';
return (
`
`
"${cmd._name}" Options:
${indent(cmd.optionHelp(), 2)}

View file

@ -79,109 +79,109 @@ export default function (program) {
const command = program.command('serve');
command
.description('Run the kibana server')
.collectUnknownOptions()
.option('-e, --elasticsearch <uri>', 'Elasticsearch instance')
.option(
'-c, --config <path>',
'Path to the config file, can be changed with the CONFIG_PATH environment variable as well. ' +
.description('Run the kibana server')
.collectUnknownOptions()
.option('-e, --elasticsearch <uri>', 'Elasticsearch instance')
.option(
'-c, --config <path>',
'Path to the config file, can be changed with the CONFIG_PATH environment variable as well. ' +
'Use multiple --config args to include multiple config files.',
configPathCollector,
[ getConfig() ]
)
.option('-p, --port <port>', 'The port to bind to', parseInt)
.option('-q, --quiet', 'Prevent all logging except errors')
.option('-Q, --silent', 'Prevent all logging')
.option('--verbose', 'Turns on verbose logging')
.option('-H, --host <host>', 'The host to bind to')
.option('-l, --log-file <path>', 'The file to log to')
.option(
'--plugin-dir <path>',
'A path to scan for plugins, this can be specified multiple ' +
configPathCollector,
[ getConfig() ]
)
.option('-p, --port <port>', 'The port to bind to', parseInt)
.option('-q, --quiet', 'Prevent all logging except errors')
.option('-Q, --silent', 'Prevent all logging')
.option('--verbose', 'Turns on verbose logging')
.option('-H, --host <host>', 'The host to bind to')
.option('-l, --log-file <path>', 'The file to log to')
.option(
'--plugin-dir <path>',
'A path to scan for plugins, this can be specified multiple ' +
'times to specify multiple directories',
pluginDirCollector,
[
fromRoot('plugins'),
fromRoot('src/core_plugins')
]
)
.option(
'--plugin-path <path>',
'A path to a plugin which should be included by the server, ' +
pluginDirCollector,
[
fromRoot('plugins'),
fromRoot('src/core_plugins')
]
)
.option(
'--plugin-path <path>',
'A path to a plugin which should be included by the server, ' +
'this can be specified multiple times to specify multiple paths',
pluginPathCollector,
[]
)
.option('--plugins <path>', 'an alias for --plugin-dir', pluginDirCollector);
pluginPathCollector,
[]
)
.option('--plugins <path>', 'an alias for --plugin-dir', pluginDirCollector);
if (canCluster) {
command
.option('--dev', 'Run the server with development mode defaults')
.option('--ssl', 'Run the dev server using HTTPS')
.option('--no-base-path', 'Don\'t put a proxy in front of the dev server, which adds a random basePath')
.option('--no-watch', 'Prevents automatic restarts of the server in --dev mode');
.option('--dev', 'Run the server with development mode defaults')
.option('--ssl', 'Run the dev server using HTTPS')
.option('--no-base-path', 'Don\'t put a proxy in front of the dev server, which adds a random basePath')
.option('--no-watch', 'Prevents automatic restarts of the server in --dev mode');
}
command
.action(async function (opts) {
if (opts.dev) {
try {
const kbnDevConfig = fromRoot('config/kibana.dev.yml');
if (statSync(kbnDevConfig).isFile()) {
opts.config.push(kbnDevConfig);
}
} catch (err) {
.action(async function (opts) {
if (opts.dev) {
try {
const kbnDevConfig = fromRoot('config/kibana.dev.yml');
if (statSync(kbnDevConfig).isFile()) {
opts.config.push(kbnDevConfig);
}
} catch (err) {
// ignore, kibana.dev.yml does not exist
}
}
const getCurrentSettings = () => readServerSettings(opts, this.getUnknownOptions());
const settings = getCurrentSettings();
if (canCluster && opts.dev && !isWorker) {
// stop processing the action and handoff to cluster manager
const ClusterManager = require('../cluster/cluster_manager');
new ClusterManager(opts, settings);
return;
}
let kbnServer = {};
const KbnServer = require('../../server/kbn_server');
try {
kbnServer = new KbnServer(settings);
await kbnServer.ready();
} catch (error) {
const { server } = kbnServer;
switch (error.code) {
case 'EADDRINUSE':
logFatal(`Port ${error.port} is already in use. Another instance of Kibana may be running!`, server);
break;
case 'InvalidConfig':
logFatal(error.message, server);
break;
default:
logFatal(error, server);
break;
}
}
kbnServer.close();
const exitCode = error.processExitCode == null ? 1 : error.processExitCode;
// eslint-disable-next-line no-process-exit
process.exit(exitCode);
}
process.on('SIGHUP', function reloadConfig() {
const getCurrentSettings = () => readServerSettings(opts, this.getUnknownOptions());
const settings = getCurrentSettings();
kbnServer.server.log(['info', 'config'], 'Reloading logging configuration due to SIGHUP.');
kbnServer.applyLoggingConfiguration(settings);
kbnServer.server.log(['info', 'config'], 'Reloaded logging configuration due to SIGHUP.');
});
return kbnServer;
});
if (canCluster && opts.dev && !isWorker) {
// stop processing the action and handoff to cluster manager
const ClusterManager = require('../cluster/cluster_manager');
new ClusterManager(opts, settings);
return;
}
let kbnServer = {};
const KbnServer = require('../../server/kbn_server');
try {
kbnServer = new KbnServer(settings);
await kbnServer.ready();
} catch (error) {
const { server } = kbnServer;
switch (error.code) {
case 'EADDRINUSE':
logFatal(`Port ${error.port} is already in use. Another instance of Kibana may be running!`, server);
break;
case 'InvalidConfig':
logFatal(error.message, server);
break;
default:
logFatal(error, server);
break;
}
kbnServer.close();
const exitCode = error.processExitCode == null ? 1 : error.processExitCode;
// eslint-disable-next-line no-process-exit
process.exit(exitCode);
}
process.on('SIGHUP', function reloadConfig() {
const settings = getCurrentSettings();
kbnServer.server.log(['info', 'config'], 'Reloading logging configuration due to SIGHUP.');
kbnServer.applyLoggingConfiguration(settings);
kbnServer.server.log(['info', 'config'], 'Reloaded logging configuration due to SIGHUP.');
});
return kbnServer;
});
}
function logFatal(message, server) {

View file

@ -9,30 +9,30 @@ const argv = process.env.kbnWorkerArgv ? JSON.parse(process.env.kbnWorkerArgv) :
const program = new Command('bin/kibana-plugin');
program
.version(pkg.version)
.description(
'The Kibana plugin manager enables you to install and remove plugins that ' +
.version(pkg.version)
.description(
'The Kibana plugin manager enables you to install and remove plugins that ' +
'provide additional functionality to Kibana'
);
);
listCommand(program);
installCommand(program);
removeCommand(program);
program
.command('help <command>')
.description('get the help for a specific command')
.action(function (cmdName) {
const cmd = _.find(program.commands, { _name: cmdName });
if (!cmd) return program.error(`unknown command ${cmdName}`);
cmd.help();
});
.command('help <command>')
.description('get the help for a specific command')
.action(function (cmdName) {
const cmd = _.find(program.commands, { _name: cmdName });
if (!cmd) return program.error(`unknown command ${cmdName}`);
cmd.help();
});
program
.command('*', null, { noHelp: true })
.action(function (cmd) {
program.error(`unknown command ${cmd}`);
});
.command('*', null, { noHelp: true })
.action(function (cmd) {
program.error(`unknown command ${cmd}`);
});
// check for no command name
const subCommand = argv[2] && !String(argv[2][0]).match(/^-|^\.|\//);

View file

@ -42,10 +42,10 @@ describe('kibana cli', function () {
});
return cleanPrevious(settings, logger)
.catch(errorStub)
.then(function () {
expect(errorStub.called).to.be(false);
});
.catch(errorStub)
.then(function () {
expect(errorStub.called).to.be(false);
});
});
it('should rethrow any exception except ENOENT from fs.statSync', function () {
@ -57,10 +57,10 @@ describe('kibana cli', function () {
errorStub = sinon.stub();
return cleanPrevious(settings, logger)
.catch(errorStub)
.then(function () {
expect(errorStub.called).to.be(true);
});
.catch(errorStub)
.then(function () {
expect(errorStub.called).to.be(true);
});
});
it('should log a message if there was a working directory', function () {
@ -68,10 +68,10 @@ describe('kibana cli', function () {
sinon.stub(fs, 'statSync');
return cleanPrevious(settings, logger)
.catch(errorStub)
.then(function () {
expect(logger.log.calledWith('Found previous install attempt. Deleting...')).to.be(true);
});
.catch(errorStub)
.then(function () {
expect(logger.log.calledWith('Found previous install attempt. Deleting...')).to.be(true);
});
});
it('should rethrow any exception from rimraf.sync', function () {
@ -82,10 +82,10 @@ describe('kibana cli', function () {
errorStub = sinon.stub();
return cleanPrevious(settings, logger)
.catch(errorStub)
.then(function () {
expect(errorStub.called).to.be(true);
});
.catch(errorStub)
.then(function () {
expect(errorStub.called).to.be(true);
});
});
it('should resolve if the working path is deleted', function () {
@ -93,10 +93,10 @@ describe('kibana cli', function () {
sinon.stub(fs, 'statSync');
return cleanPrevious(settings, logger)
.catch(errorStub)
.then(function () {
expect(errorStub.called).to.be(false);
});
.catch(errorStub)
.then(function () {
expect(errorStub.called).to.be(false);
});
});
});

View file

@ -70,20 +70,20 @@ describe('kibana cli', function () {
const sourceUrl = 'http://example.com/plugin.tar.gz';
return _downloadSingle(settings, logger, sourceUrl)
.then(shouldReject, function (err) {
expect(err.message).to.match(/ENOTFOUND/);
expectWorkingPathEmpty();
});
.then(shouldReject, function (err) {
expect(err.message).to.match(/ENOTFOUND/);
expectWorkingPathEmpty();
});
});
it('should throw an UnsupportedProtocolError for an invalid url', function () {
const sourceUrl = 'i am an invalid url';
return _downloadSingle(settings, logger, sourceUrl)
.then(shouldReject, function (err) {
expect(err).to.be.an(UnsupportedProtocolError);
expectWorkingPathEmpty();
});
.then(shouldReject, function (err) {
expect(err).to.be.an(UnsupportedProtocolError);
expectWorkingPathEmpty();
});
});
it('should download a file from a valid http url', function () {
@ -100,9 +100,9 @@ describe('kibana cli', function () {
const sourceUrl = 'http://example.com/plugin.zip';
return _downloadSingle(settings, logger, sourceUrl)
.then(function () {
expectWorkingPathNotEmpty();
});
.then(function () {
expectWorkingPathNotEmpty();
});
});
});
@ -114,10 +114,10 @@ describe('kibana cli', function () {
const sourceUrl = 'file://' + filePath.replace(/\\/g, '/');
return _downloadSingle(settings, logger, sourceUrl)
.then(shouldReject, function (err) {
expect(err.message).to.match(/ENOTFOUND/);
expectWorkingPathEmpty();
});
.then(shouldReject, function (err) {
expect(err.message).to.match(/ENOTFOUND/);
expectWorkingPathEmpty();
});
});
it('should copy a valid local file', function () {
@ -125,9 +125,9 @@ describe('kibana cli', function () {
const sourceUrl = 'file://' + filePath.replace(/\\/g, '/');
return _downloadSingle(settings, logger, sourceUrl)
.then(function () {
expectWorkingPathNotEmpty();
});
.then(function () {
expectWorkingPathNotEmpty();
});
});
});
@ -176,24 +176,24 @@ describe('kibana cli', function () {
];
nock('http://example.com')
.defaultReplyHeaders({
'content-length': '10'
})
.get('/badfile1.tar.gz')
.reply(404)
.get('/badfile2.tar.gz')
.reply(404)
.get('/goodfile.tar.gz')
.replyWithFile(200, filePath);
.defaultReplyHeaders({
'content-length': '10'
})
.get('/badfile1.tar.gz')
.reply(404)
.get('/badfile2.tar.gz')
.reply(404)
.get('/goodfile.tar.gz')
.replyWithFile(200, filePath);
return download(settings, logger)
.then(function () {
expect(logger.log.getCall(0).args[0]).to.match(/badfile1.tar.gz/);
expect(logger.log.getCall(1).args[0]).to.match(/badfile2.tar.gz/);
expect(logger.log.getCall(2).args[0]).to.match(/I am a bad uri/);
expect(logger.log.getCall(3).args[0]).to.match(/goodfile.tar.gz/);
expectWorkingPathNotEmpty();
});
.then(function () {
expect(logger.log.getCall(0).args[0]).to.match(/badfile1.tar.gz/);
expect(logger.log.getCall(1).args[0]).to.match(/badfile2.tar.gz/);
expect(logger.log.getCall(2).args[0]).to.match(/I am a bad uri/);
expect(logger.log.getCall(3).args[0]).to.match(/goodfile.tar.gz/);
expectWorkingPathNotEmpty();
});
});
it('should stop looping through urls when it finds a good one.', function () {
@ -206,25 +206,25 @@ describe('kibana cli', function () {
];
nock('http://example.com')
.defaultReplyHeaders({
'content-length': '10'
})
.get('/badfile1.tar.gz')
.reply(404)
.get('/badfile2.tar.gz')
.reply(404)
.get('/goodfile.tar.gz')
.replyWithFile(200, filePath)
.get('/badfile3.tar.gz')
.reply(404);
.defaultReplyHeaders({
'content-length': '10'
})
.get('/badfile1.tar.gz')
.reply(404)
.get('/badfile2.tar.gz')
.reply(404)
.get('/goodfile.tar.gz')
.replyWithFile(200, filePath)
.get('/badfile3.tar.gz')
.reply(404);
return download(settings, logger)
.then(function () {
for (let i = 0; i < logger.log.callCount; i++) {
expect(logger.log.getCall(i).args[0]).to.not.match(/badfile3.tar.gz/);
}
expectWorkingPathNotEmpty();
});
.then(function () {
for (let i = 0; i < logger.log.callCount; i++) {
expect(logger.log.getCall(i).args[0]).to.not.match(/badfile3.tar.gz/);
}
expectWorkingPathNotEmpty();
});
});
it('should throw an error when it doesn\'t find a good url.', function () {
@ -235,21 +235,21 @@ describe('kibana cli', function () {
];
nock('http://example.com')
.defaultReplyHeaders({
'content-length': '10'
})
.get('/badfile1.tar.gz')
.reply(404)
.get('/badfile2.tar.gz')
.reply(404)
.get('/badfile3.tar.gz')
.reply(404);
.defaultReplyHeaders({
'content-length': '10'
})
.get('/badfile1.tar.gz')
.reply(404)
.get('/badfile2.tar.gz')
.reply(404)
.get('/badfile3.tar.gz')
.reply(404);
return download(settings, logger)
.then(shouldReject, function (err) {
expect(err.message).to.match(/no valid url specified/i);
expectWorkingPathEmpty();
});
.then(shouldReject, function (err) {
expect(err.message).to.match(/no valid url specified/i);
expectWorkingPathEmpty();
});
});
after(function () {

View file

@ -66,25 +66,25 @@ describe('kibana cli', function () {
//Ignores the others.
it('successfully extract a valid zip', function () {
return copyReplyFile('test_plugin.zip')
.then(() => {
return getPackData(settings, logger);
})
.then(() => {
return extract(settings, logger);
})
.then(() => {
const files = glob.sync('**/*', { cwd: testWorkingPath });
const expected = [
'archive.part',
'README.md',
'index.js',
'package.json',
'public',
'public/app.js',
'extra file only in zip.txt'
];
expect(files.sort()).to.eql(expected.sort());
});
.then(() => {
return getPackData(settings, logger);
})
.then(() => {
return extract(settings, logger);
})
.then(() => {
const files = glob.sync('**/*', { cwd: testWorkingPath });
const expected = [
'archive.part',
'README.md',
'index.js',
'package.json',
'public',
'public/app.js',
'extra file only in zip.txt'
];
expect(files.sort()).to.eql(expected.sort());
});
});
});
@ -93,99 +93,99 @@ describe('kibana cli', function () {
it('populate settings.plugins', function () {
return copyReplyFile('test_plugin.zip')
.then(() => {
return getPackData(settings, logger);
})
.then(() => {
expect(settings.plugins[0].name).to.be('test-plugin');
expect(settings.plugins[0].archivePath).to.be('kibana/test-plugin');
expect(settings.plugins[0].version).to.be('1.0.0');
expect(settings.plugins[0].kibanaVersion).to.be('1.0.0');
});
.then(() => {
return getPackData(settings, logger);
})
.then(() => {
expect(settings.plugins[0].name).to.be('test-plugin');
expect(settings.plugins[0].archivePath).to.be('kibana/test-plugin');
expect(settings.plugins[0].version).to.be('1.0.0');
expect(settings.plugins[0].kibanaVersion).to.be('1.0.0');
});
});
it('populate settings.plugin.kibanaVersion', function () {
//kibana.version is defined in this package.json and is different than plugin version
return copyReplyFile('test_plugin_different_version.zip')
.then(() => {
return getPackData(settings, logger);
})
.then(() => {
expect(settings.plugins[0].kibanaVersion).to.be('5.0.1');
});
.then(() => {
return getPackData(settings, logger);
})
.then(() => {
expect(settings.plugins[0].kibanaVersion).to.be('5.0.1');
});
});
it('populate settings.plugin.kibanaVersion (default to plugin version)', function () {
//kibana.version is not defined in this package.json, defaults to plugin version
return copyReplyFile('test_plugin.zip')
.then(() => {
return getPackData(settings, logger);
})
.then(() => {
expect(settings.plugins[0].kibanaVersion).to.be('1.0.0');
});
.then(() => {
return getPackData(settings, logger);
})
.then(() => {
expect(settings.plugins[0].kibanaVersion).to.be('1.0.0');
});
});
it('populate settings.plugins with multiple plugins', function () {
return copyReplyFile('test_plugin_many.zip')
.then(() => {
return getPackData(settings, logger);
})
.then(() => {
expect(settings.plugins[0].name).to.be('funger-plugin');
expect(settings.plugins[0].archivePath).to.be('kibana/funger-plugin');
expect(settings.plugins[0].version).to.be('1.0.0');
.then(() => {
return getPackData(settings, logger);
})
.then(() => {
expect(settings.plugins[0].name).to.be('funger-plugin');
expect(settings.plugins[0].archivePath).to.be('kibana/funger-plugin');
expect(settings.plugins[0].version).to.be('1.0.0');
expect(settings.plugins[1].name).to.be('pdf');
expect(settings.plugins[1].archivePath).to.be('kibana/pdf-linux');
expect(settings.plugins[1].version).to.be('1.0.0');
expect(settings.plugins[1].name).to.be('pdf');
expect(settings.plugins[1].archivePath).to.be('kibana/pdf-linux');
expect(settings.plugins[1].version).to.be('1.0.0');
expect(settings.plugins[2].name).to.be('pdf');
expect(settings.plugins[2].archivePath).to.be('kibana/pdf-win32');
expect(settings.plugins[2].version).to.be('1.0.0');
expect(settings.plugins[2].name).to.be('pdf');
expect(settings.plugins[2].archivePath).to.be('kibana/pdf-win32');
expect(settings.plugins[2].version).to.be('1.0.0');
expect(settings.plugins[3].name).to.be('pdf');
expect(settings.plugins[3].archivePath).to.be('kibana/pdf-win64');
expect(settings.plugins[3].version).to.be('1.0.0');
expect(settings.plugins[3].name).to.be('pdf');
expect(settings.plugins[3].archivePath).to.be('kibana/pdf-win64');
expect(settings.plugins[3].version).to.be('1.0.0');
expect(settings.plugins[4].name).to.be('pdf');
expect(settings.plugins[4].archivePath).to.be('kibana/pdf');
expect(settings.plugins[4].version).to.be('1.0.0');
expect(settings.plugins[4].name).to.be('pdf');
expect(settings.plugins[4].archivePath).to.be('kibana/pdf');
expect(settings.plugins[4].version).to.be('1.0.0');
expect(settings.plugins[5].name).to.be('test-plugin');
expect(settings.plugins[5].archivePath).to.be('kibana/test-plugin');
expect(settings.plugins[5].version).to.be('1.0.0');
});
expect(settings.plugins[5].name).to.be('test-plugin');
expect(settings.plugins[5].archivePath).to.be('kibana/test-plugin');
expect(settings.plugins[5].version).to.be('1.0.0');
});
});
it('throw an error if there is no kibana plugin', function () {
return copyReplyFile('test_plugin_no_kibana.zip')
.then(() => {
return getPackData(settings, logger);
})
.then(shouldReject, (err) => {
expect(err.message).to.match(/No kibana plugins found in archive/i);
});
.then(() => {
return getPackData(settings, logger);
})
.then(shouldReject, (err) => {
expect(err.message).to.match(/No kibana plugins found in archive/i);
});
});
it('throw an error with a corrupt zip', function () {
return copyReplyFile('corrupt.zip')
.then(() => {
return getPackData(settings, logger);
})
.then(shouldReject, (err) => {
expect(err.message).to.match(/error retrieving/i);
});
.then(() => {
return getPackData(settings, logger);
})
.then(shouldReject, (err) => {
expect(err.message).to.match(/error retrieving/i);
});
});
it('throw an error if there an invalid plugin name', function () {
return copyReplyFile('invalid_name.zip')
.then(() => {
return getPackData(settings, logger);
})
.then(shouldReject, (err) => {
expect(err.message).to.match(/invalid plugin name/i);
});
.then(() => {
return getPackData(settings, logger);
})
.then(shouldReject, (err) => {
expect(err.message).to.match(/invalid plugin name/i);
});
});
});

View file

@ -23,10 +23,10 @@ describe('plugin folder rename', function () {
});
return renamePlugin('/foo/bar', '/bar/foo')
.catch(function (err) {
expect(err.code).to.be('error');
expect(renameStub.callCount).to.be(1);
});
.catch(function (err) {
expect(err.code).to.be('error');
expect(renameStub.callCount).to.be(1);
});
});
it('should resolve if there are no errors', function () {
@ -35,12 +35,12 @@ describe('plugin folder rename', function () {
});
return renamePlugin('/foo/bar', '/bar/foo')
.then(function () {
expect(renameStub.callCount).to.be(1);
})
.catch(function () {
throw new Error('We shouln\'t have any errors');
});
.then(function () {
expect(renameStub.callCount).to.be(1);
})
.catch(function () {
throw new Error('We shouln\'t have any errors');
});
});
describe('Windows', function () {
@ -63,10 +63,10 @@ describe('plugin folder rename', function () {
});
});
return renamePlugin('/foo/bar', '/bar/foo')
.catch(function (err) {
expect(err.code).to.be('EPERM');
expect(renameStub.callCount).to.be.greaterThan(1);
});
.catch(function (err) {
expect(err.code).to.be('EPERM');
expect(renameStub.callCount).to.be.greaterThan(1);
});
});
});
});

View file

@ -53,14 +53,14 @@ export function download(settings, logger) {
logger.log(`Attempting to transfer from ${sourceUrl}`);
return _downloadSingle(settings, logger, sourceUrl)
.catch((err) => {
const isUnsupportedProtocol = err instanceof UnsupportedProtocolError;
const isDownloadResourceNotFound = err.message === 'ENOTFOUND';
if (isUnsupportedProtocol || isDownloadResourceNotFound) {
return tryNext();
}
throw (err);
});
.catch((err) => {
const isUnsupportedProtocol = err instanceof UnsupportedProtocolError;
const isDownloadResourceNotFound = err.message === 'ENOTFOUND';
if (isUnsupportedProtocol || isDownloadResourceNotFound) {
return tryNext();
}
throw (err);
});
}
return tryNext();

View file

@ -60,4 +60,4 @@ export default async function copyLocalFile(logger, sourcePath, targetPath) {
logger.error(err);
throw err;
}
};
}

View file

@ -90,4 +90,4 @@ export default async function downloadUrl(logger, sourceUrl, targetPath, timeout
}
throw err;
}
};
}

View file

@ -22,28 +22,28 @@ function processCommand(command, options) {
export default function pluginInstall(program) {
program
.command('install <plugin/url>')
.option('-q, --quiet', 'disable all process messaging except errors')
.option('-s, --silent', 'disable all process messaging')
.option(
'-c, --config <path>',
'path to the config file',
getConfig()
)
.option(
'-t, --timeout <duration>',
'length of time before failing; 0 for never fail',
parseMilliseconds
)
.option(
'-d, --plugin-dir <path>',
'path to the directory where plugins are stored',
fromRoot('plugins')
)
.description('install a plugin',
`Common examples:
.command('install <plugin/url>')
.option('-q, --quiet', 'disable all process messaging except errors')
.option('-s, --silent', 'disable all process messaging')
.option(
'-c, --config <path>',
'path to the config file',
getConfig()
)
.option(
'-t, --timeout <duration>',
'length of time before failing; 0 for never fail',
parseMilliseconds
)
.option(
'-d, --plugin-dir <path>',
'path to the directory where plugins are stored',
fromRoot('plugins')
)
.description('install a plugin',
`Common examples:
install x-pack
install file:///Path/to/my/x-pack.zip
install https://path.to/my/x-pack.zip`)
.action(processCommand);
.action(processCommand);
}

View file

@ -3,18 +3,18 @@ import { join } from 'path';
export default function list(settings, logger) {
readdirSync(settings.pluginDir)
.forEach((filename) => {
const stat = statSync(join(settings.pluginDir, filename));
.forEach((filename) => {
const stat = statSync(join(settings.pluginDir, filename));
if (stat.isDirectory() && filename[0] !== '.') {
try {
const packagePath = join(settings.pluginDir, filename, 'package.json');
const { version } = JSON.parse(readFileSync(packagePath, 'utf8'));
logger.log(filename + '@' + version);
} catch (e) {
throw new Error('Unable to read package.json file for plugin ' + filename);
if (stat.isDirectory() && filename[0] !== '.') {
try {
const packagePath = join(settings.pluginDir, filename, 'package.json');
const { version } = JSON.parse(readFileSync(packagePath, 'utf8'));
logger.log(filename + '@' + version);
} catch (e) {
throw new Error('Unable to read package.json file for plugin ' + filename);
}
}
}
});
});
logger.log(''); //intentional blank line for aesthetics
}

View file

@ -22,21 +22,21 @@ function processCommand(command, options) {
export default function pluginRemove(program) {
program
.command('remove <plugin>')
.option('-q, --quiet', 'disable all process messaging except errors')
.option('-s, --silent', 'disable all process messaging')
.option(
'-c, --config <path>',
'path to the config file',
getConfig()
)
.option(
'-d, --plugin-dir <path>',
'path to the directory where plugins are stored',
fromRoot('plugins')
)
.description('remove a plugin',
`common examples:
.command('remove <plugin>')
.option('-q, --quiet', 'disable all process messaging except errors')
.option('-s, --silent', 'disable all process messaging')
.option(
'-c, --config <path>',
'path to the config file',
getConfig()
)
.option(
'-d, --plugin-dir <path>',
'path to the directory where plugins are stored',
fromRoot('plugins')
)
.description('remove a plugin',
`common examples:
remove x-pack`)
.action(processCommand);
.action(processCommand);
}

View file

@ -99,7 +99,7 @@ describe('es/healthCheck/patchKibanaIndex()', () => {
} catch (error) {
expect(error)
.to.have.property('message')
.contain('Your Kibana index is out of date');
.contain('Your Kibana index is out of date');
}
});
});

View file

@ -10,16 +10,16 @@ export default function (server) {
mappings: server.getKibanaIndexMappingsDsl()
}
})
.catch(() => {
throw new Error(`Unable to create Kibana index "${index}"`);
})
.then(function () {
return callWithInternalUser('cluster.health', {
waitForStatus: 'yellow',
index: index
})
.catch(() => {
throw new Error(`Waiting for Kibana index "${index}" to come online failed.`);
throw new Error(`Unable to create Kibana index "${index}"`);
})
.then(function () {
return callWithInternalUser('cluster.health', {
waitForStatus: 'yellow',
index: index
})
.catch(() => {
throw new Error(`Waiting for Kibana index "${index}" to come online failed.`);
});
});
});
}

View file

@ -27,67 +27,67 @@ export function ensureEsVersion(server, kibanaVersion) {
'nodes.*.ip',
]
})
.then(function (info) {
.then(function (info) {
// Aggregate incompatible ES nodes.
const incompatibleNodes = [];
const incompatibleNodes = [];
// Aggregate ES nodes which should prompt a Kibana upgrade.
const warningNodes = [];
// Aggregate ES nodes which should prompt a Kibana upgrade.
const warningNodes = [];
forEach(info.nodes, esNode => {
if (!isEsCompatibleWithKibana(esNode.version, kibanaVersion)) {
forEach(info.nodes, esNode => {
if (!isEsCompatibleWithKibana(esNode.version, kibanaVersion)) {
// Exit early to avoid collecting ES nodes with newer major versions in the `warningNodes`.
return incompatibleNodes.push(esNode);
}
return incompatibleNodes.push(esNode);
}
// It's acceptable if ES and Kibana versions are not the same so long as
// they are not incompatible, but we should warn about it
if (esNode.version !== kibanaVersion) {
warningNodes.push(esNode);
}
});
function getHumanizedNodeNames(nodes) {
return nodes.map(node => {
const publishAddress = get(node, 'http.publish_address') ? (get(node, 'http.publish_address') + ' ') : '';
return 'v' + node.version + ' @ ' + publishAddress + '(' + node.ip + ')';
// It's acceptable if ES and Kibana versions are not the same so long as
// they are not incompatible, but we should warn about it
if (esNode.version !== kibanaVersion) {
warningNodes.push(esNode);
}
});
}
if (warningNodes.length) {
const simplifiedNodes = warningNodes.map(node => ({
version: node.version,
http: {
publish_address: get(node, 'http.publish_address')
},
ip: node.ip,
}));
// Don't show the same warning over and over again.
const warningNodeNames = getHumanizedNodeNames(simplifiedNodes).join(', ');
if (lastWarnedNodesForServer.get(server) !== warningNodeNames) {
lastWarnedNodesForServer.set(server, warningNodeNames);
server.log(['warning'], {
tmpl: (
`You're running Kibana ${kibanaVersion} with some different versions of ` +
'Elasticsearch. Update Kibana or Elasticsearch to the same ' +
`version to prevent compatibility issues: ${warningNodeNames}`
),
kibanaVersion,
nodes: simplifiedNodes,
function getHumanizedNodeNames(nodes) {
return nodes.map(node => {
const publishAddress = get(node, 'http.publish_address') ? (get(node, 'http.publish_address') + ' ') : '';
return 'v' + node.version + ' @ ' + publishAddress + '(' + node.ip + ')';
});
}
}
if (incompatibleNodes.length) {
const incompatibleNodeNames = getHumanizedNodeNames(incompatibleNodes);
throw new Error(
`This version of Kibana requires Elasticsearch v` +
if (warningNodes.length) {
const simplifiedNodes = warningNodes.map(node => ({
version: node.version,
http: {
publish_address: get(node, 'http.publish_address')
},
ip: node.ip,
}));
// Don't show the same warning over and over again.
const warningNodeNames = getHumanizedNodeNames(simplifiedNodes).join(', ');
if (lastWarnedNodesForServer.get(server) !== warningNodeNames) {
lastWarnedNodesForServer.set(server, warningNodeNames);
server.log(['warning'], {
tmpl: (
`You're running Kibana ${kibanaVersion} with some different versions of ` +
'Elasticsearch. Update Kibana or Elasticsearch to the same ' +
`version to prevent compatibility issues: ${warningNodeNames}`
),
kibanaVersion,
nodes: simplifiedNodes,
});
}
}
if (incompatibleNodes.length) {
const incompatibleNodeNames = getHumanizedNodeNames(incompatibleNodes);
throw new Error(
`This version of Kibana requires Elasticsearch v` +
`${kibanaVersion} on all nodes. I found ` +
`the following incompatible nodes in your cluster: ${incompatibleNodeNames.join(', ')}`
);
}
);
}
return true;
});
return true;
});
}

View file

@ -5,14 +5,14 @@ export function ensureNotTribe(callWithInternalUser) {
nodeId: '_local',
filterPath: 'nodes.*.settings.tribe'
})
.then(function (info) {
const nodeId = Object.keys(info.nodes || {})[0];
const tribeSettings = get(info, ['nodes', nodeId, 'settings', 'tribe']);
.then(function (info) {
const nodeId = Object.keys(info.nodes || {})[0];
const tribeSettings = get(info, ['nodes', nodeId, 'settings', 'tribe']);
if (tribeSettings) {
throw new Error('Kibana does not support using tribe nodes as the primary elasticsearch connection.');
}
if (tribeSettings) {
throw new Error('Kibana does not support using tribe nodes as the primary elasticsearch connection.');
}
return true;
});
return true;
});
}

View file

@ -37,49 +37,49 @@ export default function (plugin, server) {
index: config.get('kibana.index'),
ignore: [408]
})
.then(function (resp) {
.then(function (resp) {
// if "timed_out" === true then elasticsearch could not
// find any idices matching our filter within 5 seconds
if (!resp || resp.timed_out) {
return NO_INDEX;
}
if (!resp || resp.timed_out) {
return NO_INDEX;
}
// If status === "red" that means that index(es) were found
// but the shards are not ready for queries
if (resp.status === 'red') {
return INITIALIZING;
}
// If status === "red" that means that index(es) were found
// but the shards are not ready for queries
if (resp.status === 'red') {
return INITIALIZING;
}
return READY;
});
return READY;
});
}
function waitUntilReady() {
return getHealth()
.then(function (health) {
if (health !== READY) {
return Promise.delay(REQUEST_DELAY).then(waitUntilReady);
}
.then(function (health) {
if (health !== READY) {
return Promise.delay(REQUEST_DELAY).then(waitUntilReady);
}
return new Promise((resolve) => {
plugin.status.once('green', resolve);
return new Promise((resolve) => {
plugin.status.once('green', resolve);
});
});
});
}
function waitForShards() {
return getHealth()
.then(function (health) {
if (health === NO_INDEX) {
plugin.status.yellow('No existing Kibana index found');
return createKibanaIndex(server);
}
.then(function (health) {
if (health === NO_INDEX) {
plugin.status.yellow('No existing Kibana index found');
return createKibanaIndex(server);
}
if (health === INITIALIZING) {
plugin.status.red('Elasticsearch is still initializing the kibana index.');
return Promise.delay(REQUEST_DELAY).then(waitForShards);
}
});
if (health === INITIALIZING) {
plugin.status.red('Elasticsearch is still initializing the kibana index.');
return Promise.delay(REQUEST_DELAY).then(waitForShards);
}
});
}
function waitForEsVersion() {
@ -96,26 +96,26 @@ export default function (plugin, server) {
function check() {
const healthCheck =
waitForPong(callAdminAsKibanaUser, config.get('elasticsearch.url'))
.then(waitForEsVersion)
.then(() => ensureNotTribe(callAdminAsKibanaUser))
.then(waitForShards)
.then(() => patchKibanaIndex({
callCluster: callAdminAsKibanaUser,
log: (...args) => server.log(...args),
indexName: config.get('kibana.index'),
kibanaIndexMappingsDsl: server.getKibanaIndexMappingsDsl()
}))
.then(() => {
const tribeUrl = config.get('elasticsearch.tribe.url');
if (tribeUrl) {
return waitForPong(callDataAsKibanaUser, tribeUrl)
.then(() => ensureEsVersion(server, kibanaVersion.get(), callDataAsKibanaUser));
}
});
.then(waitForEsVersion)
.then(() => ensureNotTribe(callAdminAsKibanaUser))
.then(waitForShards)
.then(() => patchKibanaIndex({
callCluster: callAdminAsKibanaUser,
log: (...args) => server.log(...args),
indexName: config.get('kibana.index'),
kibanaIndexMappingsDsl: server.getKibanaIndexMappingsDsl()
}))
.then(() => {
const tribeUrl = config.get('elasticsearch.tribe.url');
if (tribeUrl) {
return waitForPong(callDataAsKibanaUser, tribeUrl)
.then(() => ensureEsVersion(server, kibanaVersion.get(), callDataAsKibanaUser));
}
});
return healthCheck
.then(setGreenStatus)
.catch(err => plugin.status.red(err));
.then(setGreenStatus)
.catch(err => plugin.status.red(err));
}

View file

@ -55,16 +55,16 @@ export class PhraseFilterManager extends FilterManager {
// bool filter - multiple phrase filters
if (_.has(kbnFilter, 'query.bool.should')) {
return _.get(kbnFilter, 'query.bool.should')
.map((kbnFilter) => {
return this._getValueFromFilter(kbnFilter);
})
.filter((value) => {
if (value) {
return true;
}
return false;
})
.join(this.delimiter);
.map((kbnFilter) => {
return this._getValueFromFilter(kbnFilter);
})
.filter((value) => {
if (value) {
return true;
}
return false;
})
.join(this.delimiter);
}
// scripted field filter

View file

@ -49,10 +49,10 @@ class VisController {
// ignore controls that do not have indexPattern or field
return controlParams.indexPattern && controlParams.fieldName;
})
.map((controlParams) => {
const factory = controlFactory(controlParams);
return factory(controlParams, this.vis.API);
})
.map((controlParams) => {
const factory = controlFactory(controlParams);
return factory(controlParams, this.vis.API);
})
);
}
@ -73,12 +73,12 @@ class VisController {
});
const newFilters = stagedControls
.filter((control) => {
return control.hasKbnFilter();
})
.map((control) => {
return control.getKbnFilter();
});
.filter((control) => {
return control.hasKbnFilter();
})
.map((control) => {
return control.getKbnFilter();
});
stagedControls.forEach((control) => {
// to avoid duplicate filters, remove any old filters for control
@ -108,18 +108,18 @@ class VisController {
return this.controls.map((control) => {
return control.hasChanged();
})
.reduce((a, b) => {
return a || b;
});
.reduce((a, b) => {
return a || b;
});
}
hasValues() {
return this.controls.map((control) => {
return control.hasValue();
})
.reduce((a, b) => {
return a || b;
});
.reduce((a, b) => {
return a || b;
});
}
}

View file

@ -93,8 +93,8 @@ UrlFormat.prototype._convert = {
// is tell screen readers where the image comes from.
const imageLabel =
label === url
? `A dynamically-specified image located at ${url}`
: label;
? `A dynamically-specified image located at ${url}`
: label;
return `<img src="${url}" alt="${imageLabel}">`;
default:

View file

@ -1,7 +1,7 @@
plugins: [
'@elastic/kibana-custom'
'@elastic/eslint-plugin-kibana-custom'
]
rules:
no-console: 2
kibana-custom/no-default-export: error
'@elastic/kibana-custom/no-default-export': error

View file

@ -21,10 +21,10 @@ module.directive('contextSizePicker', function ContextSizePicker() {
count: '=',
isDisabled: '=',
onChangeCount: '=', // To avoid inconsistent ngModel states this action
// should make sure the new value is propagated back
// to the `count` property. If that propagation
// fails, the user input will be reset to the value
// of `count`.
// should make sure the new value is propagated back
// to the `count` property. If that propagation
// fails, the user input will be reset to the value
// of `count`.
},
template: contextSizePickerTemplate,
};

View file

@ -9,16 +9,16 @@ import contextAppRouteTemplate from './index.html';
uiRoutes
.when('/context/:indexPatternId/:type/:id*', {
controller: ContextAppRouteController,
controllerAs: 'contextAppRoute',
resolve: {
indexPattern: function ($route, courier) {
return courier.indexPatterns.get($route.current.params.indexPatternId);
.when('/context/:indexPatternId/:type/:id*', {
controller: ContextAppRouteController,
controllerAs: 'contextAppRoute',
resolve: {
indexPattern: function ($route, courier) {
return courier.indexPatterns.get($route.current.params.indexPatternId);
},
},
},
template: contextAppRouteTemplate,
});
template: contextAppRouteTemplate,
});
function ContextAppRouteController(

View file

@ -1,3 +1,4 @@
/* global jest */
export function getEmbeddableFactoryMock(config) {
const embeddableFactoryMockDefaults = {
getEditPath: () => {},

View file

@ -59,4 +59,3 @@ test('renders DashboardGrid with no visualizations', () => {
const component = shallow(<DashboardGrid {...getProps({ panels: {} })} />);
expect(component).toMatchSnapshot();
});

View file

@ -1,3 +1,4 @@
import expect from 'expect.js';
import { PanelUtils } from '../panel_utils';
import { DEFAULT_PANEL_WIDTH, DEFAULT_PANEL_HEIGHT } from '../../dashboard_constants';

View file

@ -48,4 +48,3 @@ test('renders an error when error prop is passed', () => {
const panelError = component.find(PanelError);
expect(panelError.length).toBe(1);
});

View file

@ -14,15 +14,15 @@ export function getTopNavConfig(dashboardMode, actions, hideWriteControls) {
case DashboardViewMode.VIEW:
return (
hideWriteControls ?
[
getFullScreenConfig(actions[TopNavIds.FULL_SCREEN])
]
: [
getFullScreenConfig(actions[TopNavIds.FULL_SCREEN]),
getShareConfig(),
getCloneConfig(actions[TopNavIds.CLONE]),
getEditConfig(actions[TopNavIds.ENTER_EDIT_MODE])
]
[
getFullScreenConfig(actions[TopNavIds.FULL_SCREEN])
]
: [
getFullScreenConfig(actions[TopNavIds.FULL_SCREEN]),
getShareConfig(),
getCloneConfig(actions[TopNavIds.CLONE]),
getEditConfig(actions[TopNavIds.ENTER_EDIT_MODE])
]
);
case DashboardViewMode.EDIT:
return [

View file

@ -5,23 +5,23 @@ import 'plugins/kibana/dev_tools/styles/dev_tools_app.less';
import 'ui/kbn_top_nav';
uiModules
.get('apps/dev_tools')
.directive('kbnDevToolsApp', function (Private, $location) {
const devToolsRegistry = Private(DevToolsRegistryProvider);
.get('apps/dev_tools')
.directive('kbnDevToolsApp', function (Private, $location) {
const devToolsRegistry = Private(DevToolsRegistryProvider);
return {
restrict: 'E',
replace: true,
template,
transclude: true,
scope: {
topNavConfig: '='
},
bindToController: true,
controllerAs: 'kbnDevToolsApp',
controller() {
this.devTools = devToolsRegistry.inOrder;
this.currentPath = `#${$location.path()}`;
}
};
});
return {
restrict: 'E',
replace: true,
template,
transclude: true,
scope: {
topNavConfig: '='
},
bindToController: true,
controllerAs: 'kbnDevToolsApp',
controller() {
this.devTools = devToolsRegistry.inOrder;
this.currentPath = `#${$location.path()}`;
}
};
});

View file

@ -4,14 +4,14 @@ import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/r
import 'plugins/kibana/dev_tools/directives/dev_tools_app';
uiRoutes
.when('/dev_tools', {
resolve: {
redirect(Private, kbnUrl) {
const items = Private(DevToolsRegistryProvider).inOrder;
kbnUrl.redirect(items[0].url.substring(1));
.when('/dev_tools', {
resolve: {
redirect(Private, kbnUrl) {
const items = Private(DevToolsRegistryProvider).inOrder;
kbnUrl.redirect(items[0].url.substring(1));
}
}
}
});
});
FeatureCatalogueRegistryProvider.register(() => {
return {

View file

@ -31,10 +31,10 @@ describe('hit sort function', function () {
});
hits.sort(createHitSortFn(dir))
.forEach(function (hit, i) {
const group = Math.floor(i / groupSize);
expect(hit.sort).to.eql(sortOpts[group]);
});
.forEach(function (hit, i) {
const group = Math.floor(i / groupSize);
expect(hit.sort).to.eql(sortOpts[group]);
});
};

View file

@ -1,4 +1,4 @@
// eslint-disable-next-line kibana-custom/no-default-export
// eslint-disable-next-line @elastic/kibana-custom/no-default-export
export default function HitSortFnFactory() {
/**
* Creates a sort function that will resort hits based on the value

View file

@ -121,32 +121,32 @@ app.directive('discFieldChooser', function ($location, globalState, config, $rou
// group the fields into popular and up-popular lists
_.chain(fields)
.each(function (field) {
field.displayOrder = _.indexOf(columns, field.name) + 1;
field.display = !!field.displayOrder;
field.rowCount = fieldCounts[field.name];
})
.sortBy(function (field) {
return (field.count || 0) * -1;
})
.groupBy(function (field) {
if (field.display) return 'selected';
return field.count > 0 ? 'popular' : 'unpopular';
})
.tap(function (groups) {
groups.selected = _.sortBy(groups.selected || [], 'displayOrder');
.each(function (field) {
field.displayOrder = _.indexOf(columns, field.name) + 1;
field.display = !!field.displayOrder;
field.rowCount = fieldCounts[field.name];
})
.sortBy(function (field) {
return (field.count || 0) * -1;
})
.groupBy(function (field) {
if (field.display) return 'selected';
return field.count > 0 ? 'popular' : 'unpopular';
})
.tap(function (groups) {
groups.selected = _.sortBy(groups.selected || [], 'displayOrder');
groups.popular = groups.popular || [];
groups.unpopular = groups.unpopular || [];
groups.popular = groups.popular || [];
groups.unpopular = groups.unpopular || [];
// move excess popular fields to un-popular list
const extras = groups.popular.splice(config.get('fields:popularLimit'));
groups.unpopular = extras.concat(groups.unpopular);
})
.each(function (group, name) {
$scope[name + 'Fields'] = _.sortBy(group, name === 'selected' ? 'display' : 'name');
})
.commit();
// move excess popular fields to un-popular list
const extras = groups.popular.splice(config.get('fields:popularLimit'));
groups.unpopular = extras.concat(groups.unpopular);
})
.each(function (group, name) {
$scope[name + 'Fields'] = _.sortBy(group, name === 'selected' ? 'display' : 'name');
})
.commit();
// include undefined so the user can clear the filter
$scope.fieldTypes = _.union(['any'], _.pluck(fields, 'type'));
@ -245,12 +245,12 @@ app.directive('discFieldChooser', function ($location, globalState, config, $rou
const fieldNamesInIndexPattern = _.keys(indexPattern.fields.byName);
_.difference(fieldNamesInDocs, fieldNamesInIndexPattern)
.forEach(function (unknownFieldName) {
fieldSpecs.push({
name: unknownFieldName,
type: 'unknown'
.forEach(function (unknownFieldName) {
fieldSpecs.push({
name: unknownFieldName,
type: 'unknown'
});
});
});
const fields = new FieldList(indexPattern, fieldSpecs);

View file

@ -38,24 +38,24 @@ const app = uiModules.get('apps/discover', [
]);
uiRoutes
.defaults(/discover/, {
requireDefaultIndex: true
})
.when('/discover/:id?', {
template: indexTemplate,
reloadOnSearch: false,
resolve: {
ip: function (Promise, courier, config, $location, Private) {
const State = Private(StateProvider);
const savedObjectsClient = Private(SavedObjectsClientProvider);
.defaults(/discover/, {
requireDefaultIndex: true
})
.when('/discover/:id?', {
template: indexTemplate,
reloadOnSearch: false,
resolve: {
ip: function (Promise, courier, config, $location, Private) {
const State = Private(StateProvider);
const savedObjectsClient = Private(SavedObjectsClientProvider);
return savedObjectsClient.find({
type: 'index-pattern',
fields: ['title'],
perPage: 10000
})
.then(({ savedObjects }) => {
/**
return savedObjectsClient.find({
type: 'index-pattern',
fields: ['title'],
perPage: 10000
})
.then(({ savedObjects }) => {
/**
* In making the indexPattern modifiable it was placed in appState. Unfortunately,
* the load order of AppState conflicts with the load order of many other things
* so in order to get the name of the index we should use, and to switch to the
@ -64,30 +64,30 @@ uiRoutes
*
* @type {State}
*/
const state = new State('_a', {});
const state = new State('_a', {});
const specified = !!state.index;
const exists = _.findIndex(savedObjects, o => o.id === state.index) > -1;
const id = exists ? state.index : config.get('defaultIndex');
state.destroy();
const specified = !!state.index;
const exists = _.findIndex(savedObjects, o => o.id === state.index) > -1;
const id = exists ? state.index : config.get('defaultIndex');
state.destroy();
return Promise.props({
list: savedObjects,
loaded: courier.indexPatterns.get(id),
stateVal: state.index,
stateValFound: specified && exists
});
});
},
savedSearch: function (courier, savedSearches, $route) {
return savedSearches.get($route.current.params.id)
.catch(courier.redirectWhenMissing({
'search': '/discover',
'index-pattern': '/management/kibana/objects/savedSearches/' + $route.current.params.id
}));
return Promise.props({
list: savedObjects,
loaded: courier.indexPatterns.get(id),
stateVal: state.index,
stateValFound: specified && exists
});
});
},
savedSearch: function (courier, savedSearches, $route) {
return savedSearches.get($route.current.params.id)
.catch(courier.redirectWhenMissing({
'search': '/discover',
'index-pattern': '/management/kibana/objects/savedSearches/' + $route.current.params.id
}));
}
}
}
});
});
app.directive('discoverApp', function () {
return {
@ -163,9 +163,9 @@ function discoverController(
$scope.searchSource = savedSearch.searchSource;
$scope.indexPattern = resolveIndexPatternLoading();
$scope.searchSource
.set('index', $scope.indexPattern)
.highlightAll(true)
.version(true);
.set('index', $scope.indexPattern)
.highlightAll(true)
.version(true);
const pageTitleSuffix = savedSearch.id && savedSearch.title ? `: ${savedSearch.title}` : '';
docTitle.change(`Discover${pageTitleSuffix}`);
@ -293,138 +293,138 @@ function discoverController(
$scope.$on('$destroy', () => stateMonitor.destroy());
$scope.updateDataSource()
.then(function () {
$scope.$listen(timefilter, 'fetch', function () {
$scope.fetch();
});
$scope.$watchCollection('state.sort', function (sort) {
if (!sort) return;
// get the current sort from {key: val} to ["key", "val"];
const currentSort = _.pairs($scope.searchSource.get('sort')).pop();
// if the searchSource doesn't know, tell it so
if (!angular.equals(sort, currentSort)) $scope.fetch();
});
// update data source when filters update
$scope.$listen(queryFilter, 'update', function () {
return $scope.updateDataSource().then(function () {
$state.save();
});
});
// update data source when hitting forward/back and the query changes
$scope.$listen($state, 'fetch_with_changes', function (diff) {
if (diff.indexOf('query') >= 0) $scope.fetch();
});
// fetch data when filters fire fetch event
$scope.$listen(queryFilter, 'fetch', $scope.fetch);
$scope.$watch('opts.timefield', function (timefield) {
timefilter.enabled = !!timefield;
});
$scope.$watch('state.interval', function () {
$scope.fetch();
});
$scope.$watch('vis.aggs', function () {
// no timefield, no vis, nothing to update
if (!$scope.opts.timefield) return;
const buckets = $scope.vis.getAggConfig().bySchemaGroup.buckets;
if (buckets && buckets.length === 1) {
$scope.bucketInterval = buckets[0].buckets.getInterval();
}
});
$scope.$watch('state.query', $scope.updateQueryAndFetch);
$scope.$watchMulti([
'rows',
'fetchStatus'
], (function updateResultState() {
let prev = {};
const status = {
LOADING: 'loading', // initial data load
READY: 'ready', // results came back
NO_RESULTS: 'none' // no results came back
};
function pick(rows, oldRows, fetchStatus) {
// initial state, pretend we are loading
if (rows == null && oldRows == null) return status.LOADING;
const rowsEmpty = _.isEmpty(rows);
// An undefined fetchStatus means the requests are still being
// prepared to be sent. When all requests are completed,
// fetchStatus is set to null, so it's important that we
// specifically check for undefined to determine a loading status.
const preparingForFetch = _.isUndefined(fetchStatus);
if (preparingForFetch) return status.LOADING;
else if (rowsEmpty && fetchStatus) return status.LOADING;
else if (!rowsEmpty) return status.READY;
else return status.NO_RESULTS;
}
return function () {
const current = {
rows: $scope.rows,
fetchStatus: $scope.fetchStatus
};
$scope.resultState = pick(
current.rows,
prev.rows,
current.fetchStatus,
prev.fetchStatus
);
prev = current;
};
}()));
function initForTime() {
return setupVisualization().then($scope.updateTime);
}
return Promise.resolve($scope.opts.timefield && initForTime())
.then(function () {
init.complete = true;
$state.replace();
$scope.$emit('application.load');
$scope.$listen(timefilter, 'fetch', function () {
$scope.fetch();
});
$scope.$watchCollection('state.sort', function (sort) {
if (!sort) return;
// get the current sort from {key: val} to ["key", "val"];
const currentSort = _.pairs($scope.searchSource.get('sort')).pop();
// if the searchSource doesn't know, tell it so
if (!angular.equals(sort, currentSort)) $scope.fetch();
});
// update data source when filters update
$scope.$listen(queryFilter, 'update', function () {
return $scope.updateDataSource().then(function () {
$state.save();
});
});
// update data source when hitting forward/back and the query changes
$scope.$listen($state, 'fetch_with_changes', function (diff) {
if (diff.indexOf('query') >= 0) $scope.fetch();
});
// fetch data when filters fire fetch event
$scope.$listen(queryFilter, 'fetch', $scope.fetch);
$scope.$watch('opts.timefield', function (timefield) {
timefilter.enabled = !!timefield;
});
$scope.$watch('state.interval', function () {
$scope.fetch();
});
$scope.$watch('vis.aggs', function () {
// no timefield, no vis, nothing to update
if (!$scope.opts.timefield) return;
const buckets = $scope.vis.getAggConfig().bySchemaGroup.buckets;
if (buckets && buckets.length === 1) {
$scope.bucketInterval = buckets[0].buckets.getInterval();
}
});
$scope.$watch('state.query', $scope.updateQueryAndFetch);
$scope.$watchMulti([
'rows',
'fetchStatus'
], (function updateResultState() {
let prev = {};
const status = {
LOADING: 'loading', // initial data load
READY: 'ready', // results came back
NO_RESULTS: 'none' // no results came back
};
function pick(rows, oldRows, fetchStatus) {
// initial state, pretend we are loading
if (rows == null && oldRows == null) return status.LOADING;
const rowsEmpty = _.isEmpty(rows);
// An undefined fetchStatus means the requests are still being
// prepared to be sent. When all requests are completed,
// fetchStatus is set to null, so it's important that we
// specifically check for undefined to determine a loading status.
const preparingForFetch = _.isUndefined(fetchStatus);
if (preparingForFetch) return status.LOADING;
else if (rowsEmpty && fetchStatus) return status.LOADING;
else if (!rowsEmpty) return status.READY;
else return status.NO_RESULTS;
}
return function () {
const current = {
rows: $scope.rows,
fetchStatus: $scope.fetchStatus
};
$scope.resultState = pick(
current.rows,
prev.rows,
current.fetchStatus,
prev.fetchStatus
);
prev = current;
};
}()));
function initForTime() {
return setupVisualization().then($scope.updateTime);
}
return Promise.resolve($scope.opts.timefield && initForTime())
.then(function () {
init.complete = true;
$state.replace();
$scope.$emit('application.load');
});
});
});
});
$scope.opts.saveDataSource = function () {
return $scope.updateDataSource()
.then(function () {
savedSearch.columns = $scope.state.columns;
savedSearch.sort = $scope.state.sort;
.then(function () {
savedSearch.columns = $scope.state.columns;
savedSearch.sort = $scope.state.sort;
return savedSearch.save()
.then(function (id) {
stateMonitor.setInitialState($state.toJSON());
$scope.kbnTopNav.close('save');
return savedSearch.save()
.then(function (id) {
stateMonitor.setInitialState($state.toJSON());
$scope.kbnTopNav.close('save');
if (id) {
notify.info('Saved Data Source "' + savedSearch.title + '"');
if (savedSearch.id !== $route.current.params.id) {
kbnUrl.change('/discover/{{id}}', { id: savedSearch.id });
} else {
// Update defaults so that "reload saved query" functions correctly
$state.setDefaults(getStateDefaults());
docTitle.change(savedSearch.lastSavedTitle);
}
}
});
})
.catch(notify.error);
if (id) {
notify.info('Saved Data Source "' + savedSearch.title + '"');
if (savedSearch.id !== $route.current.params.id) {
kbnUrl.change('/discover/{{id}}', { id: savedSearch.id });
} else {
// Update defaults so that "reload saved query" functions correctly
$state.setDefaults(getStateDefaults());
docTitle.change(savedSearch.lastSavedTitle);
}
}
});
})
.catch(notify.error);
};
$scope.opts.fetch = $scope.fetch = function () {
@ -434,12 +434,12 @@ function discoverController(
$scope.updateTime();
$scope.updateDataSource()
.then(setupVisualization)
.then(function () {
$state.save();
return courier.fetch();
})
.catch(notify.error);
.then(setupVisualization)
.then(function () {
$state.save();
return courier.fetch();
})
.catch(notify.error);
};
$scope.updateQueryAndFetch = function (query) {
@ -592,10 +592,10 @@ function discoverController(
$scope.updateDataSource = Promise.method(function updateDataSource() {
$scope.searchSource
.size($scope.opts.sampleSize)
.sort(getSort($state.sort, $scope.indexPattern))
.query(!$state.query ? null : $state.query)
.set('filter', queryFilter.getFilters());
.size($scope.opts.sampleSize)
.sort(getSort($state.sort, $scope.indexPattern))
.query(!$state.query ? null : $state.query)
.set('filter', queryFilter.getFilters());
});
$scope.setSortOrder = function setSortOrder(columnName, direction) {
@ -691,10 +691,10 @@ function discoverController(
resolve($scope.vis);
});
})
.finally(function () {
.finally(function () {
// clear the loading flag
loadingVis = null;
});
loadingVis = null;
});
return loadingVis;
}

View file

@ -3,10 +3,10 @@ import noResultsTemplate from '../partials/no_results.html';
import 'ui/directives/documentation_href';
uiModules
.get('apps/discover')
.directive('discoverNoResults', function () {
return {
restrict: 'E',
template: noResultsTemplate
};
});
.get('apps/discover')
.directive('discoverNoResults', function () {
return {
restrict: 'E',
template: noResultsTemplate
};
});

View file

@ -1,32 +1,32 @@
import VislibProvider from 'ui/vislib';
import { uiModules } from 'ui/modules';
uiModules
.get('apps/discover')
.directive('discoverTimechart', function (Private) {
const vislib = Private(VislibProvider);
.get('apps/discover')
.directive('discoverTimechart', function (Private) {
const vislib = Private(VislibProvider);
return {
restrict: 'E',
scope: {
data: '='
},
link: function ($scope, elem) {
return {
restrict: 'E',
scope: {
data: '='
},
link: function ($scope, elem) {
const init = function () {
const init = function () {
// This elem should already have a height/width
const myChart = new vislib.Chart(elem[0], {
addLegend: false
});
const myChart = new vislib.Chart(elem[0], {
addLegend: false
});
$scope.$watch('data', function (data) {
if (data != null) {
myChart.render(data);
}
});
};
$scope.$watch('data', function (data) {
if (data != null) {
myChart.render(data);
}
});
};
// Start the directive
init();
}
};
});
// Start the directive
init();
}
};
});

View file

@ -21,14 +21,14 @@ const resolveIndexPattern = {
};
uiRoutes
.when('/doc/:indexPattern/:index/:type/:id', {
template: html,
resolve: resolveIndexPattern
})
.when('/doc/:indexPattern/:index/:type', {
template: html,
resolve: resolveIndexPattern
});
.when('/doc/:indexPattern/:index/:type/:id', {
template: html,
resolve: resolveIndexPattern
})
.when('/doc/:indexPattern/:index/:type', {
template: html,
resolve: resolveIndexPattern
});
app.controller('doc', function ($scope, $route, es, timefilter) {

View file

@ -24,7 +24,7 @@ const formatIds = [
'relative_date'
];
// eslint-disable-next-line kibana-custom/no-default-export
// eslint-disable-next-line @elastic/kibana-custom/no-default-export
export default describe('conformance', function () {
beforeEach(ngMock.module('kibana'));

View file

@ -13,30 +13,30 @@ describe('Duration Format', function () {
}));
test({ inputFormat: 'seconds', outputFormat: 'humanize' })
(-60, 'minus a minute')
(60, 'a minute')
(125, '2 minutes');
(-60, 'minus a minute')
(60, 'a minute')
(125, '2 minutes');
test({ inputFormat: 'minutes', outputFormat: 'humanize' })
(-60, 'minus an hour')
(60, 'an hour')
(125, '2 hours');
(-60, 'minus an hour')
(60, 'an hour')
(125, '2 hours');
test({ inputFormat: 'minutes', outputFormat: 'asHours' }) // outputPrecision defaults to: 2
(-60, '-1.00')
(60, '1.00')
(125, '2.08');
(-60, '-1.00')
(60, '1.00')
(125, '2.08');
test({ inputFormat: 'seconds', outputFormat: 'asSeconds', outputPrecision: 0 })
(-60, '-60')
(60, '60')
(125, '125');
(-60, '-60')
(60, '60')
(125, '125');
test({ inputFormat: 'seconds', outputFormat: 'asSeconds', outputPrecision: 2 })
(-60, '-60.00')
(-32.333, '-32.33')
(60, '60.00')
(125, '125.00');
(-60, '-60.00')
(-32.333, '-32.33')
(60, '60.00')
(125, '125.00');
function test({ inputFormat, outputFormat, outputPrecision }) {
return function testFixture(input, output) {

View file

@ -13,21 +13,21 @@ export function Home({ addBasePath, directories }) {
const renderDirectories = (category) => {
return directories
.filter((directory) => {
return directory.showOnHomePage && directory.category === category;
})
.map((directory) => {
return (
<KuiFlexItem style={{ minHeight: 64 }} key={directory.id}>
<Synopsis
description={directory.description}
iconUrl={addBasePath(directory.icon)}
title={directory.title}
url={addBasePath(directory.path)}
/>
</KuiFlexItem>
);
});
.filter((directory) => {
return directory.showOnHomePage && directory.category === category;
})
.map((directory) => {
return (
<KuiFlexItem style={{ minHeight: 64 }} key={directory.id}>
<Synopsis
description={directory.description}
iconUrl={addBasePath(directory.icon)}
title={directory.title}
url={addBasePath(directory.path)}
/>
</KuiFlexItem>
);
});
};

View file

@ -25,9 +25,9 @@ import { KibanaRootController } from './kibana_root_controller';
routes.enable();
routes
.otherwise({
redirectTo: `/${chrome.getInjected('kbnDefaultAppId', 'discover')}`
});
.otherwise({
redirectTo: `/${chrome.getInjected('kbnDefaultAppId', 'discover')}`
});
chrome.setRootController('kibana', KibanaRootController);

View file

@ -10,54 +10,54 @@ import { management } from 'ui/management';
import 'ui/kbn_top_nav';
uiRoutes
.when('/management', {
template: landingTemplate
});
.when('/management', {
template: landingTemplate
});
uiRoutes
.when('/management/:section', {
redirectTo: '/management'
});
.when('/management/:section', {
redirectTo: '/management'
});
require('ui/index_patterns/route_setup/load_default')({
whenMissingRedirectTo: '/management/kibana/index'
});
uiModules
.get('apps/management')
.directive('kbnManagementApp', function (Private, $location, timefilter) {
return {
restrict: 'E',
template: appTemplate,
transclude: true,
scope: {
sectionName: '@section',
omitPages: '@omitBreadcrumbPages',
pageTitle: '='
},
.get('apps/management')
.directive('kbnManagementApp', function (Private, $location, timefilter) {
return {
restrict: 'E',
template: appTemplate,
transclude: true,
scope: {
sectionName: '@section',
omitPages: '@omitBreadcrumbPages',
pageTitle: '='
},
link: function ($scope) {
timefilter.enabled = false;
$scope.sections = management.items.inOrder;
$scope.section = management.getSection($scope.sectionName) || management;
link: function ($scope) {
timefilter.enabled = false;
$scope.sections = management.items.inOrder;
$scope.section = management.getSection($scope.sectionName) || management;
if ($scope.section) {
$scope.section.items.forEach(item => {
item.active = `#${$location.path()}`.indexOf(item.url) > -1;
});
if ($scope.section) {
$scope.section.items.forEach(item => {
item.active = `#${$location.path()}`.indexOf(item.url) > -1;
});
}
}
}
};
});
};
});
uiModules
.get('apps/management')
.directive('kbnManagementLanding', function (kbnVersion) {
return {
restrict: 'E',
link: function ($scope) {
$scope.sections = management.items.inOrder;
$scope.kbnVersion = kbnVersion;
}
};
});
.get('apps/management')
.directive('kbnManagementLanding', function (kbnVersion) {
return {
restrict: 'E',
link: function ($scope) {
$scope.sections = management.items.inOrder;
$scope.kbnVersion = kbnVersion;
}
};
});

View file

@ -12,276 +12,276 @@ import './step_time_field';
import './matching_indices_list';
uiRoutes
.when('/management/kibana/index', {
template,
});
.when('/management/kibana/index', {
template,
});
uiModules.get('apps/management')
.controller('managementIndicesCreate', function (
$routeParams,
$scope,
$timeout,
config,
es,
indexPatterns,
kbnUrl,
Notifier,
Promise
) {
.controller('managementIndicesCreate', function (
$routeParams,
$scope,
$timeout,
config,
es,
indexPatterns,
kbnUrl,
Notifier,
Promise
) {
// This isn't ideal. We want to avoid searching for 20 indices
// then filtering out the majority of them because they are sysetm indices.
// We'd like to filter system indices out in the query
// so if we can accomplish that in the future, this logic can go away
const ESTIMATED_NUMBER_OF_SYSTEM_INDICES = 20;
const MAX_NUMBER_OF_MATCHING_INDICES = 20;
const MAX_SEARCH_SIZE = MAX_NUMBER_OF_MATCHING_INDICES + ESTIMATED_NUMBER_OF_SYSTEM_INDICES;
const notify = new Notifier();
const disabledDividerOption = {
isDisabled: true,
display: '───',
};
const noTimeFieldOption = {
display: `I don't want to use the Time Filter`,
};
const ESTIMATED_NUMBER_OF_SYSTEM_INDICES = 20;
const MAX_NUMBER_OF_MATCHING_INDICES = 20;
const MAX_SEARCH_SIZE = MAX_NUMBER_OF_MATCHING_INDICES + ESTIMATED_NUMBER_OF_SYSTEM_INDICES;
const notify = new Notifier();
const disabledDividerOption = {
isDisabled: true,
display: '───',
};
const noTimeFieldOption = {
display: `I don't want to use the Time Filter`,
};
// Configure the new index pattern we're going to create.
this.formValues = {
id: $routeParams.id ? decodeURIComponent($routeParams.id) : undefined,
name: '',
expandWildcard: false,
timeFieldOption: undefined,
};
// Configure the new index pattern we're going to create.
this.formValues = {
id: $routeParams.id ? decodeURIComponent($routeParams.id) : undefined,
name: '',
expandWildcard: false,
timeFieldOption: undefined,
};
// UI state.
this.timeFieldOptions = [];
this.wizardStep = 'indexPattern';
this.isFetchingExistingIndices = true;
this.isFetchingMatchingIndices = false;
this.isFetchingTimeFieldOptions = false;
this.isCreatingIndexPattern = false;
this.doesIncludeSystemIndices = false;
let allIndices = [];
let matchingIndices = [];
let partialMatchingIndices = [];
this.allIndices = [];
this.matchingIndices = [];
this.partialMatchingIndices = [];
// UI state.
this.timeFieldOptions = [];
this.wizardStep = 'indexPattern';
this.isFetchingExistingIndices = true;
this.isFetchingMatchingIndices = false;
this.isFetchingTimeFieldOptions = false;
this.isCreatingIndexPattern = false;
this.doesIncludeSystemIndices = false;
let allIndices = [];
let matchingIndices = [];
let partialMatchingIndices = [];
this.allIndices = [];
this.matchingIndices = [];
this.partialMatchingIndices = [];
function createReasonableWait() {
return new Promise(resolve => {
function createReasonableWait() {
return new Promise(resolve => {
// Make every fetch take a set amount of time so the user gets some feedback that something
// is happening.
$timeout(() => {
resolve();
}, 500);
});
}
$timeout(() => {
resolve();
}, 500);
});
}
function getIndices(pattern, limit = MAX_SEARCH_SIZE) {
const params = {
index: pattern,
ignore: [404],
body: {
size: 0, // no hits
aggs: {
indices: {
terms: {
field: '_index',
size: limit,
function getIndices(pattern, limit = MAX_SEARCH_SIZE) {
const params = {
index: pattern,
ignore: [404],
body: {
size: 0, // no hits
aggs: {
indices: {
terms: {
field: '_index',
size: limit,
}
}
}
}
};
return es.search(params)
.then(response => {
if (!response || response.error || !response.aggregations) {
return [];
}
return _.sortBy(response.aggregations.indices.buckets.map(bucket => {
return {
name: bucket.key
};
}), 'name');
});
}
const whiteListIndices = indices => {
if (!indices) {
return indices;
}
const acceptableIndices = this.doesIncludeSystemIndices
? indices
// All system indices begin with a period.
: indices.filter(index => !index.name.startsWith('.'));
return acceptableIndices.slice(0, MAX_NUMBER_OF_MATCHING_INDICES);
};
return es.search(params)
.then(response => {
if (!response || response.error || !response.aggregations) {
return [];
const updateWhiteListedIndices = () => {
this.allIndices = whiteListIndices(allIndices);
this.matchingIndices = whiteListIndices(matchingIndices);
this.partialMatchingIndices = whiteListIndices(partialMatchingIndices);
};
this.onIncludeSystemIndicesChange = () => {
updateWhiteListedIndices();
};
let mostRecentFetchMatchingIndicesRequest;
this.fetchMatchingIndices = () => {
this.isFetchingMatchingIndices = true;
// Default to searching for all indices.
const exactSearchQuery = this.formValues.name;
let partialSearchQuery = this.formValues.name;
if (!_.endsWith(partialSearchQuery, '*')) {
partialSearchQuery = `${partialSearchQuery}*`;
}
if (!_.startsWith(partialSearchQuery, '*')) {
partialSearchQuery = `*${partialSearchQuery}`;
}
const thisFetchMatchingIndicesRequest = mostRecentFetchMatchingIndicesRequest = Promise.all([
getIndices(exactSearchQuery),
getIndices(partialSearchQuery),
createReasonableWait()
])
.then(([
matchingIndicesResponse,
partialMatchingIndicesResponse
]) => {
if (thisFetchMatchingIndicesRequest === mostRecentFetchMatchingIndicesRequest) {
matchingIndices = matchingIndicesResponse;
partialMatchingIndices = partialMatchingIndicesResponse;
updateWhiteListedIndices();
this.isFetchingMatchingIndices = false;
}
}).catch(error => {
notify.error(error);
});
};
this.fetchExistingIndices = () => {
this.isFetchingExistingIndices = true;
const allExistingLocalIndicesPattern = '*';
Promise.all([
getIndices(allExistingLocalIndicesPattern),
createReasonableWait()
])
.then(([allIndicesResponse]) => {
// Cache all indices.
allIndices = allIndicesResponse;
updateWhiteListedIndices();
this.isFetchingExistingIndices = false;
}).catch(error => {
notify.error(error);
this.isFetchingExistingIndices = false;
});
};
this.isSystemIndicesCheckBoxVisible = () => (
this.wizardStep === 'indexPattern'
);
this.goToIndexPatternStep = () => {
this.wizardStep = 'indexPattern';
};
this.goToTimeFieldStep = () => {
// Re-initialize this step.
this.formValues.timeFieldOption = undefined;
this.fetchTimeFieldOptions();
this.wizardStep = 'timeField';
};
this.hasIndices = () => (
this.allIndices.length
);
const extractTimeFieldsFromFields = fields => {
const dateFields = fields.filter(field => field.type === 'date');
if (dateFields.length === 0) {
return [{
display: `The indices which match this index pattern don't contain any time fields.`,
}];
}
return [
...dateFields.map(field => ({
display: field.name,
fieldName: field.name
})),
disabledDividerOption,
noTimeFieldOption,
];
};
this.fetchTimeFieldOptions = () => {
this.isFetchingTimeFieldOptions = true;
this.formValues.timeFieldOption = undefined;
this.timeFieldOptions = [];
Promise.all([
indexPatterns.fieldsFetcher.fetchForWildcard(this.formValues.name),
createReasonableWait(),
])
.then(([fields]) => {
this.timeFieldOptions = extractTimeFieldsFromFields(fields);
})
.catch(error => {
notify.error(error);
})
.finally(() => {
this.isFetchingTimeFieldOptions = false;
});
};
this.createIndexPattern = () => {
this.isCreatingIndexPattern = true;
const {
id,
name,
timeFieldOption,
} = this.formValues;
const timeFieldName = timeFieldOption
? timeFieldOption.fieldName
: undefined;
sendCreateIndexPatternRequest(indexPatterns, {
id,
name,
timeFieldName,
}).then(createdId => {
if (!createdId) {
return;
}
return _.sortBy(response.aggregations.indices.buckets.map(bucket => {
return {
name: bucket.key
};
}), 'name');
if (!config.get('defaultIndex')) {
config.set('defaultIndex', createdId);
}
indexPatterns.cache.clear(createdId);
kbnUrl.change(`/management/kibana/indices/${createdId}`);
}).catch(err => {
if (err instanceof IndexPatternMissingIndices) {
return notify.error(`Couldn't locate any indices matching that pattern. Please add the index to Elasticsearch`);
}
notify.fatal(err);
}).finally(() => {
this.isCreatingIndexPattern = false;
});
}
};
const whiteListIndices = indices => {
if (!indices) {
return indices;
}
const acceptableIndices = this.doesIncludeSystemIndices
? indices
// All system indices begin with a period.
: indices.filter(index => !index.name.startsWith('.'));
return acceptableIndices.slice(0, MAX_NUMBER_OF_MATCHING_INDICES);
};
const updateWhiteListedIndices = () => {
this.allIndices = whiteListIndices(allIndices);
this.matchingIndices = whiteListIndices(matchingIndices);
this.partialMatchingIndices = whiteListIndices(partialMatchingIndices);
};
this.onIncludeSystemIndicesChange = () => {
updateWhiteListedIndices();
};
let mostRecentFetchMatchingIndicesRequest;
this.fetchMatchingIndices = () => {
this.isFetchingMatchingIndices = true;
// Default to searching for all indices.
const exactSearchQuery = this.formValues.name;
let partialSearchQuery = this.formValues.name;
if (!_.endsWith(partialSearchQuery, '*')) {
partialSearchQuery = `${partialSearchQuery}*`;
}
if (!_.startsWith(partialSearchQuery, '*')) {
partialSearchQuery = `*${partialSearchQuery}`;
}
const thisFetchMatchingIndicesRequest = mostRecentFetchMatchingIndicesRequest = Promise.all([
getIndices(exactSearchQuery),
getIndices(partialSearchQuery),
createReasonableWait()
])
.then(([
matchingIndicesResponse,
partialMatchingIndicesResponse
]) => {
if (thisFetchMatchingIndicesRequest === mostRecentFetchMatchingIndicesRequest) {
matchingIndices = matchingIndicesResponse;
partialMatchingIndices = partialMatchingIndicesResponse;
updateWhiteListedIndices();
this.isFetchingMatchingIndices = false;
}
}).catch(error => {
notify.error(error);
});
};
this.fetchExistingIndices = () => {
this.isFetchingExistingIndices = true;
const allExistingLocalIndicesPattern = '*';
Promise.all([
getIndices(allExistingLocalIndicesPattern),
createReasonableWait()
])
.then(([allIndicesResponse]) => {
// Cache all indices.
allIndices = allIndicesResponse;
updateWhiteListedIndices();
this.isFetchingExistingIndices = false;
}).catch(error => {
notify.error(error);
this.isFetchingExistingIndices = false;
});
};
this.isSystemIndicesCheckBoxVisible = () => (
this.wizardStep === 'indexPattern'
);
this.goToIndexPatternStep = () => {
this.wizardStep = 'indexPattern';
};
this.goToTimeFieldStep = () => {
// Re-initialize this step.
this.formValues.timeFieldOption = undefined;
this.fetchTimeFieldOptions();
this.wizardStep = 'timeField';
};
this.hasIndices = () => (
this.allIndices.length
);
const extractTimeFieldsFromFields = fields => {
const dateFields = fields.filter(field => field.type === 'date');
if (dateFields.length === 0) {
return [{
display: `The indices which match this index pattern don't contain any time fields.`,
}];
}
return [
...dateFields.map(field => ({
display: field.name,
fieldName: field.name
})),
disabledDividerOption,
noTimeFieldOption,
];
};
this.fetchTimeFieldOptions = () => {
this.isFetchingTimeFieldOptions = true;
this.formValues.timeFieldOption = undefined;
this.timeFieldOptions = [];
Promise.all([
indexPatterns.fieldsFetcher.fetchForWildcard(this.formValues.name),
createReasonableWait(),
])
.then(([fields]) => {
this.timeFieldOptions = extractTimeFieldsFromFields(fields);
})
.catch(error => {
notify.error(error);
})
.finally(() => {
this.isFetchingTimeFieldOptions = false;
});
};
this.createIndexPattern = () => {
this.isCreatingIndexPattern = true;
const {
id,
name,
timeFieldOption,
} = this.formValues;
const timeFieldName = timeFieldOption
? timeFieldOption.fieldName
: undefined;
sendCreateIndexPatternRequest(indexPatterns, {
id,
name,
timeFieldName,
}).then(createdId => {
if (!createdId) {
return;
}
if (!config.get('defaultIndex')) {
config.set('defaultIndex', createdId);
}
indexPatterns.cache.clear(createdId);
kbnUrl.change(`/management/kibana/indices/${createdId}`);
}).catch(err => {
if (err instanceof IndexPatternMissingIndices) {
return notify.error(`Couldn't locate any indices matching that pattern. Please add the index to Elasticsearch`);
}
notify.fatal(err);
}).finally(() => {
this.isCreatingIndexPattern = false;
});
};
this.fetchExistingIndices();
});
this.fetchExistingIndices();
});

View file

@ -11,130 +11,130 @@ import { uiModules } from 'ui/modules';
import template from './edit_index_pattern.html';
uiRoutes
.when('/management/kibana/indices/:indexPatternId', {
template,
resolve: {
indexPattern: function ($route, courier) {
return courier.indexPatterns
.get($route.current.params.indexPatternId)
.catch(courier.redirectWhenMissing('/management/kibana/index'));
.when('/management/kibana/indices/:indexPatternId', {
template,
resolve: {
indexPattern: function ($route, courier) {
return courier.indexPatterns
.get($route.current.params.indexPatternId)
.catch(courier.redirectWhenMissing('/management/kibana/index'));
}
}
}
});
});
uiRoutes
.when('/management/kibana/indices', {
resolve: {
redirect: function ($location, config) {
const defaultIndex = config.get('defaultIndex');
let path = '/management/kibana/index';
.when('/management/kibana/indices', {
resolve: {
redirect: function ($location, config) {
const defaultIndex = config.get('defaultIndex');
let path = '/management/kibana/index';
if (defaultIndex) {
path = `/management/kibana/indices/${defaultIndex}`;
if (defaultIndex) {
path = `/management/kibana/indices/${defaultIndex}`;
}
$location.path(path).replace();
}
$location.path(path).replace();
}
}
});
});
uiModules.get('apps/management')
.controller('managementIndicesEdit', function (
.controller('managementIndicesEdit', function (
$scope, $location, $route, config, courier, Notifier, Private, AppState, docTitle, confirmModal) {
const notify = new Notifier();
const $state = $scope.state = new AppState();
const notify = new Notifier();
const $state = $scope.state = new AppState();
$scope.kbnUrl = Private(KbnUrlProvider);
$scope.indexPattern = $route.current.locals.indexPattern;
docTitle.change($scope.indexPattern.title);
$scope.kbnUrl = Private(KbnUrlProvider);
$scope.indexPattern = $route.current.locals.indexPattern;
docTitle.change($scope.indexPattern.title);
const otherPatterns = _.filter($route.current.locals.indexPatterns, pattern => {
return pattern.id !== $scope.indexPattern.id;
});
$scope.$watch('indexPattern.fields', function () {
$scope.editSections = Private(IndicesEditSectionsProvider)($scope.indexPattern);
$scope.refreshFilters();
});
$scope.refreshFilters = function () {
const indexedFieldTypes = [];
const scriptedFieldLanguages = [];
$scope.indexPattern.fields.forEach(field => {
if (field.scripted) {
scriptedFieldLanguages.push(field.lang);
} else {
indexedFieldTypes.push(field.type);
}
const otherPatterns = _.filter($route.current.locals.indexPatterns, pattern => {
return pattern.id !== $scope.indexPattern.id;
});
$scope.indexedFieldTypes = _.unique(indexedFieldTypes);
$scope.scriptedFieldLanguages = _.unique(scriptedFieldLanguages);
};
$scope.$watch('indexPattern.fields', function () {
$scope.editSections = Private(IndicesEditSectionsProvider)($scope.indexPattern);
$scope.refreshFilters();
});
$scope.changeFilter = function (filter, val) {
$scope[filter] = val || ''; // null causes filter to check for null explicitly
};
$scope.changeTab = function (obj) {
$state.tab = obj.index;
$state.save();
};
$scope.$watch('state.tab', function (tab) {
if (!tab) $scope.changeTab($scope.editSections[0]);
});
$scope.$watchCollection('indexPattern.fields', function () {
$scope.conflictFields = $scope.indexPattern.fields
.filter(field => field.type === 'conflict');
});
$scope.refreshFields = function () {
const confirmModalOptions = {
confirmButtonText: 'Refresh fields',
onConfirm: () => { $scope.indexPattern.refreshFields(); }
};
confirmModal(
'This will reset the field popularity counters. Are you sure you want to refresh your fields?',
confirmModalOptions
);
};
$scope.removePattern = function () {
function doRemove() {
if ($scope.indexPattern.id === config.get('defaultIndex')) {
config.remove('defaultIndex');
if (otherPatterns.length) {
config.set('defaultIndex', otherPatterns[0].id);
$scope.refreshFilters = function () {
const indexedFieldTypes = [];
const scriptedFieldLanguages = [];
$scope.indexPattern.fields.forEach(field => {
if (field.scripted) {
scriptedFieldLanguages.push(field.lang);
} else {
indexedFieldTypes.push(field.type);
}
});
$scope.indexedFieldTypes = _.unique(indexedFieldTypes);
$scope.scriptedFieldLanguages = _.unique(scriptedFieldLanguages);
};
$scope.changeFilter = function (filter, val) {
$scope[filter] = val || ''; // null causes filter to check for null explicitly
};
$scope.changeTab = function (obj) {
$state.tab = obj.index;
$state.save();
};
$scope.$watch('state.tab', function (tab) {
if (!tab) $scope.changeTab($scope.editSections[0]);
});
$scope.$watchCollection('indexPattern.fields', function () {
$scope.conflictFields = $scope.indexPattern.fields
.filter(field => field.type === 'conflict');
});
$scope.refreshFields = function () {
const confirmModalOptions = {
confirmButtonText: 'Refresh fields',
onConfirm: () => { $scope.indexPattern.refreshFields(); }
};
confirmModal(
'This will reset the field popularity counters. Are you sure you want to refresh your fields?',
confirmModalOptions
);
};
$scope.removePattern = function () {
function doRemove() {
if ($scope.indexPattern.id === config.get('defaultIndex')) {
config.remove('defaultIndex');
if (otherPatterns.length) {
config.set('defaultIndex', otherPatterns[0].id);
}
}
courier.indexPatterns.delete($scope.indexPattern)
.then(function () {
$location.url('/management/kibana/index');
})
.catch(notify.fatal);
}
courier.indexPatterns.delete($scope.indexPattern)
.then(function () {
$location.url('/management/kibana/index');
})
.catch(notify.fatal);
}
const confirmModalOptions = {
confirmButtonText: 'Remove index pattern',
onConfirm: doRemove
const confirmModalOptions = {
confirmButtonText: 'Remove index pattern',
onConfirm: doRemove
};
confirmModal('Are you sure you want to remove this index pattern?', confirmModalOptions);
};
confirmModal('Are you sure you want to remove this index pattern?', confirmModalOptions);
};
$scope.setDefaultPattern = function () {
config.set('defaultIndex', $scope.indexPattern.id);
};
$scope.setDefaultPattern = function () {
config.set('defaultIndex', $scope.indexPattern.id);
};
$scope.setIndexPatternsTimeField = function (field) {
if (field.type !== 'date') {
notify.error('That field is a ' + field.type + ' not a date.');
return;
}
$scope.indexPattern.timeFieldName = field.name;
return $scope.indexPattern.save();
};
});
$scope.setIndexPatternsTimeField = function (field) {
if (field.type !== 'date') {
notify.error('That field is a ' + field.type + ' not a date.');
return;
}
$scope.indexPattern.timeFieldName = field.name;
return $scope.indexPattern.save();
};
});

View file

@ -1,23 +1,23 @@
import { uiModules } from 'ui/modules';
import template from './index_header.html';
uiModules
.get('apps/management')
.directive('kbnManagementIndexHeader', function (config) {
return {
restrict: 'E',
template,
replace: true,
scope: {
indexPattern: '=',
setDefault: '&',
refreshFields: '&',
delete: '&',
},
link: function ($scope, $el, attrs) {
$scope.delete = attrs.delete ? $scope.delete : null;
$scope.setDefault = attrs.setDefault ? $scope.setDefault : null;
$scope.refreshFields = attrs.refreshFields ? $scope.refreshFields : null;
config.bindToScope($scope, 'defaultIndex');
}
};
});
.get('apps/management')
.directive('kbnManagementIndexHeader', function (config) {
return {
restrict: 'E',
template,
replace: true,
scope: {
indexPattern: '=',
setDefault: '&',
refreshFields: '&',
delete: '&',
},
link: function ($scope, $el, attrs) {
$scope.delete = attrs.delete ? $scope.delete : null;
$scope.setDefault = attrs.setDefault ? $scope.setDefault : null;
$scope.refreshFields = attrs.refreshFields ? $scope.refreshFields : null;
config.bindToScope($scope, 'defaultIndex');
}
};
});

View file

@ -8,85 +8,85 @@ import { FieldWildcardProvider } from 'ui/field_wildcard';
import template from './indexed_fields_table.html';
uiModules.get('apps/management')
.directive('indexedFieldsTable', function (Private, $filter) {
const yesTemplate = '<i class="fa fa-check" aria-label="yes"></i>';
const noTemplate = '';
const filter = $filter('filter');
const { fieldWildcardMatcher } = Private(FieldWildcardProvider);
.directive('indexedFieldsTable', function (Private, $filter) {
const yesTemplate = '<i class="fa fa-check" aria-label="yes"></i>';
const noTemplate = '';
const filter = $filter('filter');
const { fieldWildcardMatcher } = Private(FieldWildcardProvider);
return {
restrict: 'E',
template,
scope: true,
link: function ($scope) {
const rowScopes = []; // track row scopes, so they can be destroyed as needed
$scope.perPage = 25;
$scope.columns = [
{ title: 'name' },
{ title: 'type' },
{ title: 'format' },
{ title: 'searchable', info: 'These fields can be used in the filter bar' },
{ title: 'aggregatable', info: 'These fields can be used in visualization aggregations' },
{ title: 'excluded', info: 'Fields that are excluded from _source when it is fetched' },
{ title: 'controls', sortable: false }
];
return {
restrict: 'E',
template,
scope: true,
link: function ($scope) {
const rowScopes = []; // track row scopes, so they can be destroyed as needed
$scope.perPage = 25;
$scope.columns = [
{ title: 'name' },
{ title: 'type' },
{ title: 'format' },
{ title: 'searchable', info: 'These fields can be used in the filter bar' },
{ title: 'aggregatable', info: 'These fields can be used in visualization aggregations' },
{ title: 'excluded', info: 'Fields that are excluded from _source when it is fetched' },
{ title: 'controls', sortable: false }
];
$scope.$watchMulti(['[]indexPattern.fields', 'fieldFilter', 'indexedFieldTypeFilter'], refreshRows);
$scope.$watchMulti(['[]indexPattern.fields', 'fieldFilter', 'indexedFieldTypeFilter'], refreshRows);
function refreshRows() {
function refreshRows() {
// clear and destroy row scopes
_.invoke(rowScopes.splice(0), '$destroy');
const fields = filter($scope.indexPattern.getNonScriptedFields(), {
name: $scope.fieldFilter,
type: $scope.indexedFieldTypeFilter
});
const sourceFilters = $scope.indexPattern.sourceFilters && $scope.indexPattern.sourceFilters.map(f => f.value) || [];
const fieldWildcardMatch = fieldWildcardMatcher(sourceFilters);
_.find($scope.editSections, { index: 'indexedFields' }).count = fields.length; // Update the tab count
_.invoke(rowScopes.splice(0), '$destroy');
const fields = filter($scope.indexPattern.getNonScriptedFields(), {
name: $scope.fieldFilter,
type: $scope.indexedFieldTypeFilter
});
const sourceFilters = $scope.indexPattern.sourceFilters && $scope.indexPattern.sourceFilters.map(f => f.value) || [];
const fieldWildcardMatch = fieldWildcardMatcher(sourceFilters);
_.find($scope.editSections, { index: 'indexedFields' }).count = fields.length; // Update the tab count
$scope.rows = fields.map(function (field) {
const childScope = _.assign($scope.$new(), { field: field });
rowScopes.push(childScope);
$scope.rows = fields.map(function (field) {
const childScope = _.assign($scope.$new(), { field: field });
rowScopes.push(childScope);
const excluded = fieldWildcardMatch(field.name);
const excluded = fieldWildcardMatch(field.name);
return [
{
markup: fieldNameHtml,
scope: childScope,
value: field.displayName,
attr: {
'data-test-subj': 'indexedFieldName'
return [
{
markup: fieldNameHtml,
scope: childScope,
value: field.displayName,
attr: {
'data-test-subj': 'indexedFieldName'
}
},
{
markup: fieldTypeHtml,
scope: childScope,
value: field.type,
attr: {
'data-test-subj': 'indexedFieldType'
}
},
_.get($scope.indexPattern, ['fieldFormatMap', field.name, 'type', 'title']),
{
markup: field.searchable ? yesTemplate : noTemplate,
value: field.searchable
},
{
markup: field.aggregatable ? yesTemplate : noTemplate,
value: field.aggregatable
},
{
markup: excluded ? yesTemplate : noTemplate,
value: excluded
},
{
markup: fieldControlsHtml,
scope: childScope
}
},
{
markup: fieldTypeHtml,
scope: childScope,
value: field.type,
attr: {
'data-test-subj': 'indexedFieldType'
}
},
_.get($scope.indexPattern, ['fieldFormatMap', field.name, 'type', 'title']),
{
markup: field.searchable ? yesTemplate : noTemplate,
value: field.searchable
},
{
markup: field.aggregatable ? yesTemplate : noTemplate,
value: field.aggregatable
},
{
markup: excluded ? yesTemplate : noTemplate,
value: excluded
},
{
markup: fieldControlsHtml,
scope: childScope
}
];
});
];
});
}
}
}
};
});
};
});

View file

@ -5,63 +5,63 @@ import uiRoutes from 'ui/routes';
import template from './scripted_field_editor.html';
uiRoutes
.when('/management/kibana/indices/:indexPatternId/field/:fieldName*', { mode: 'edit' })
.when('/management/kibana/indices/:indexPatternId/create-field/', { mode: 'create' })
.defaults(/management\/kibana\/indices\/[^\/]+\/(field|create-field)(\/|$)/, {
template,
mapBreadcrumbs($route, breadcrumbs) {
const { indexPattern } = $route.current.locals;
return breadcrumbs.map(crumb => {
if (crumb.id !== indexPattern.id) {
return crumb;
}
.when('/management/kibana/indices/:indexPatternId/field/:fieldName*', { mode: 'edit' })
.when('/management/kibana/indices/:indexPatternId/create-field/', { mode: 'create' })
.defaults(/management\/kibana\/indices\/[^\/]+\/(field|create-field)(\/|$)/, {
template,
mapBreadcrumbs($route, breadcrumbs) {
const { indexPattern } = $route.current.locals;
return breadcrumbs.map(crumb => {
if (crumb.id !== indexPattern.id) {
return crumb;
}
return {
...crumb,
display: indexPattern.title
};
});
},
resolve: {
indexPattern: function ($route, courier) {
return courier.indexPatterns.get($route.current.params.indexPatternId)
.catch(courier.redirectWhenMissing('/management/kibana/indices'));
}
},
controllerAs: 'fieldSettings',
controller: function FieldEditorPageController($route, Private, Notifier, docTitle) {
const Field = Private(IndexPatternsFieldProvider);
const notify = new Notifier({ location: 'Field Editor' });
const kbnUrl = Private(KbnUrlProvider);
this.mode = $route.current.mode;
this.indexPattern = $route.current.locals.indexPattern;
if (this.mode === 'edit') {
const fieldName = $route.current.params.fieldName;
this.field = this.indexPattern.fields.byName[fieldName];
if (!this.field) {
notify.error(this.indexPattern + ' does not have a "' + fieldName + '" field.');
kbnUrl.redirectToRoute(this.indexPattern, 'edit');
return;
}
}
else if (this.mode === 'create') {
this.field = new Field(this.indexPattern, {
scripted: true,
type: 'number'
return {
...crumb,
display: indexPattern.title
};
});
}
else {
throw new Error('unknown fieldSettings mode ' + this.mode);
}
},
resolve: {
indexPattern: function ($route, courier) {
return courier.indexPatterns.get($route.current.params.indexPatternId)
.catch(courier.redirectWhenMissing('/management/kibana/indices'));
}
},
controllerAs: 'fieldSettings',
controller: function FieldEditorPageController($route, Private, Notifier, docTitle) {
const Field = Private(IndexPatternsFieldProvider);
const notify = new Notifier({ location: 'Field Editor' });
const kbnUrl = Private(KbnUrlProvider);
docTitle.change([this.field.name || 'New Scripted Field', this.indexPattern.title]);
this.goBack = function () {
kbnUrl.changeToRoute(this.indexPattern, 'edit');
};
}
});
this.mode = $route.current.mode;
this.indexPattern = $route.current.locals.indexPattern;
if (this.mode === 'edit') {
const fieldName = $route.current.params.fieldName;
this.field = this.indexPattern.fields.byName[fieldName];
if (!this.field) {
notify.error(this.indexPattern + ' does not have a "' + fieldName + '" field.');
kbnUrl.redirectToRoute(this.indexPattern, 'edit');
return;
}
}
else if (this.mode === 'create') {
this.field = new Field(this.indexPattern, {
scripted: true,
type: 'number'
});
}
else {
throw new Error('unknown fieldSettings mode ' + this.mode);
}
docTitle.change([this.field.name || 'New Scripted Field', this.indexPattern.title]);
this.goBack = function () {
kbnUrl.changeToRoute(this.indexPattern, 'edit');
};
}
});

View file

@ -1,3 +1,4 @@
import _ from 'lodash';
import 'ui/paginated_table';
import fieldControlsHtml from '../field_controls.html';
@ -8,124 +9,124 @@ import { getSupportedScriptingLanguages, getDeprecatedScriptingLanguages } from
import { documentationLinks } from 'ui/documentation_links/documentation_links';
uiModules.get('apps/management')
.directive('scriptedFieldsTable', function (kbnUrl, Notifier, $filter, confirmModal) {
const rowScopes = []; // track row scopes, so they can be destroyed as needed
const filter = $filter('filter');
.directive('scriptedFieldsTable', function (kbnUrl, Notifier, $filter, confirmModal) {
const rowScopes = []; // track row scopes, so they can be destroyed as needed
const filter = $filter('filter');
const notify = new Notifier();
const notify = new Notifier();
return {
restrict: 'E',
template,
scope: true,
link: function ($scope) {
return {
restrict: 'E',
template,
scope: true,
link: function ($scope) {
const fieldCreatorPath = '/management/kibana/indices/{{ indexPattern }}/scriptedField';
const fieldEditorPath = fieldCreatorPath + '/{{ fieldName }}';
const fieldCreatorPath = '/management/kibana/indices/{{ indexPattern }}/scriptedField';
const fieldEditorPath = fieldCreatorPath + '/{{ fieldName }}';
$scope.docLinks = documentationLinks.scriptedFields;
$scope.perPage = 25;
$scope.columns = [
{ title: 'name' },
{ title: 'lang' },
{ title: 'script' },
{ title: 'format' },
{ title: 'controls', sortable: false }
];
$scope.docLinks = documentationLinks.scriptedFields;
$scope.perPage = 25;
$scope.columns = [
{ title: 'name' },
{ title: 'lang' },
{ title: 'script' },
{ title: 'format' },
{ title: 'controls', sortable: false }
];
$scope.$watchMulti(['[]indexPattern.fields', 'fieldFilter', 'scriptedFieldLanguageFilter'], refreshRows);
$scope.$watchMulti(['[]indexPattern.fields', 'fieldFilter', 'scriptedFieldLanguageFilter'], refreshRows);
function refreshRows() {
_.invoke(rowScopes, '$destroy');
rowScopes.length = 0;
function refreshRows() {
_.invoke(rowScopes, '$destroy');
rowScopes.length = 0;
const fields = filter($scope.indexPattern.getScriptedFields(), {
name: $scope.fieldFilter,
lang: $scope.scriptedFieldLanguageFilter
});
_.find($scope.editSections, { index: 'scriptedFields' }).count = fields.length; // Update the tab count
const fields = filter($scope.indexPattern.getScriptedFields(), {
name: $scope.fieldFilter,
lang: $scope.scriptedFieldLanguageFilter
});
_.find($scope.editSections, { index: 'scriptedFields' }).count = fields.length; // Update the tab count
$scope.rows = fields.map(function (field) {
const rowScope = $scope.$new();
rowScope.field = field;
rowScopes.push(rowScope);
$scope.rows = fields.map(function (field) {
const rowScope = $scope.$new();
rowScope.field = field;
rowScopes.push(rowScope);
return [
_.escape(field.name),
{
markup: field.lang,
attr: {
'data-test-subj': 'scriptedFieldLang'
return [
_.escape(field.name),
{
markup: field.lang,
attr: {
'data-test-subj': 'scriptedFieldLang'
}
},
_.escape(field.script),
_.get($scope.indexPattern, ['fieldFormatMap', field.name, 'type', 'title']),
{
markup: fieldControlsHtml,
scope: rowScope
}
},
_.escape(field.script),
_.get($scope.indexPattern, ['fieldFormatMap', field.name, 'type', 'title']),
{
markup: fieldControlsHtml,
scope: rowScope
];
});
}
$scope.addDateScripts = function () {
const conflictFields = [];
let fieldsAdded = 0;
_.each(dateScripts($scope.indexPattern), function (script, field) {
try {
$scope.indexPattern.addScriptedField(field, script, 'number');
fieldsAdded++;
} catch (e) {
conflictFields.push(field);
}
];
});
}
});
$scope.addDateScripts = function () {
const conflictFields = [];
let fieldsAdded = 0;
_.each(dateScripts($scope.indexPattern), function (script, field) {
try {
$scope.indexPattern.addScriptedField(field, script, 'number');
fieldsAdded++;
} catch (e) {
conflictFields.push(field);
if (fieldsAdded > 0) {
notify.info(fieldsAdded + ' script fields created');
}
});
if (fieldsAdded > 0) {
notify.info(fieldsAdded + ' script fields created');
if (conflictFields.length > 0) {
notify.info('Not adding ' + conflictFields.length + ' duplicate fields: ' + conflictFields.join(', '));
}
};
$scope.create = function () {
const params = {
indexPattern: $scope.indexPattern.id
};
kbnUrl.change(fieldCreatorPath, params);
};
$scope.edit = function (field) {
const params = {
indexPattern: $scope.indexPattern.id,
fieldName: field.name
};
kbnUrl.change(fieldEditorPath, params);
};
$scope.remove = function (field) {
const confirmModalOptions = {
confirmButtonText: 'Delete field',
onConfirm: () => { $scope.indexPattern.removeScriptedField(field.name); }
};
confirmModal(`Are you sure want to delete ${field.name}? This action is irreversible!`, confirmModalOptions);
};
function getLanguagesInUse() {
const fields = $scope.indexPattern.getScriptedFields();
return _.uniq(_.map(fields, 'lang'));
}
if (conflictFields.length > 0) {
notify.info('Not adding ' + conflictFields.length + ' duplicate fields: ' + conflictFields.join(', '));
}
};
$scope.create = function () {
const params = {
indexPattern: $scope.indexPattern.id
$scope.getDeprecatedLanguagesInUse = function () {
return _.intersection(getLanguagesInUse(), getDeprecatedScriptingLanguages());
};
kbnUrl.change(fieldCreatorPath, params);
};
$scope.edit = function (field) {
const params = {
indexPattern: $scope.indexPattern.id,
fieldName: field.name
$scope.getUnsupportedLanguagesInUse = function () {
return _.difference(getLanguagesInUse(), _.union(getSupportedScriptingLanguages(), getDeprecatedScriptingLanguages()));
};
kbnUrl.change(fieldEditorPath, params);
};
$scope.remove = function (field) {
const confirmModalOptions = {
confirmButtonText: 'Delete field',
onConfirm: () => { $scope.indexPattern.removeScriptedField(field.name); }
};
confirmModal(`Are you sure want to delete ${field.name}? This action is irreversible!`, confirmModalOptions);
};
function getLanguagesInUse() {
const fields = $scope.indexPattern.getScriptedFields();
return _.uniq(_.map(fields, 'lang'));
}
$scope.getDeprecatedLanguagesInUse = function () {
return _.intersection(getLanguagesInUse(), getDeprecatedScriptingLanguages());
};
$scope.getUnsupportedLanguagesInUse = function () {
return _.difference(getLanguagesInUse(), _.union(getSupportedScriptingLanguages(), getDeprecatedScriptingLanguages()));
};
}
};
});
};
});

View file

@ -12,116 +12,116 @@ import './source_filters_table.less';
const notify = new Notifier();
uiModules.get('kibana')
.directive('sourceFiltersTable', function (Private, $filter, confirmModal) {
const angularFilter = $filter('filter');
const { fieldWildcardMatcher } = Private(FieldWildcardProvider);
const rowScopes = []; // track row scopes, so they can be destroyed as needed
.directive('sourceFiltersTable', function (Private, $filter, confirmModal) {
const angularFilter = $filter('filter');
const { fieldWildcardMatcher } = Private(FieldWildcardProvider);
const rowScopes = []; // track row scopes, so they can be destroyed as needed
return {
restrict: 'E',
scope: {
indexPattern: '='
},
template,
controllerAs: 'sourceFilters',
controller: class FieldFiltersController {
constructor($scope) {
if (!$scope.indexPattern) {
throw new Error('index pattern is required');
return {
restrict: 'E',
scope: {
indexPattern: '='
},
template,
controllerAs: 'sourceFilters',
controller: class FieldFiltersController {
constructor($scope) {
if (!$scope.indexPattern) {
throw new Error('index pattern is required');
}
$scope.perPage = 25;
$scope.columns = [
{
title: 'filter'
},
{
title: 'matches',
sortable: false,
info: 'The source fields that match the filter.'
},
{
title: 'controls',
sortable: false
}
];
this.$scope = $scope;
this.saving = false;
this.editing = null;
this.newValue = null;
this.placeHolder = 'source filter, accepts wildcards (e.g., `user*` to filter fields starting with \'user\')';
$scope.$watchMulti([ '[]indexPattern.sourceFilters', '$parent.fieldFilter' ], () => {
invoke(rowScopes, '$destroy');
rowScopes.length = 0;
if ($scope.indexPattern.sourceFilters) {
$scope.rows = [];
each($scope.indexPattern.sourceFilters, (filter) => {
const matcher = fieldWildcardMatcher([ filter.value ]);
// compute which fields match a filter
const matches = $scope.indexPattern.getNonScriptedFields().map(f => f.name).filter(matcher).sort();
if ($scope.$parent.fieldFilter && !angularFilter(matches, $scope.$parent.fieldFilter).length) {
return;
}
// compute the rows
const rowScope = $scope.$new();
rowScope.filter = filter;
rowScopes.push(rowScope);
$scope.rows.push([
{
markup: filterHtml,
scope: rowScope
},
size(matches) ? escape(matches.join(', ')) : '<em>The source filter doesn\'t match any known fields.</em>',
{
markup: controlsHtml,
scope: rowScope
}
]);
});
// Update the tab count
find($scope.$parent.editSections, { index: 'sourceFilters' }).count = $scope.rows.length;
}
});
}
$scope.perPage = 25;
$scope.columns = [
{
title: 'filter'
},
{
title: 'matches',
sortable: false,
info: 'The source fields that match the filter.'
},
{
title: 'controls',
sortable: false
}
];
all() {
return this.$scope.indexPattern.sourceFilters || [];
}
this.$scope = $scope;
this.saving = false;
this.editing = null;
this.newValue = null;
this.placeHolder = 'source filter, accepts wildcards (e.g., `user*` to filter fields starting with \'user\')';
delete(filter) {
const doDelete = () => {
if (this.editing === filter) {
this.editing = null;
}
$scope.$watchMulti([ '[]indexPattern.sourceFilters', '$parent.fieldFilter' ], () => {
invoke(rowScopes, '$destroy');
rowScopes.length = 0;
this.$scope.indexPattern.sourceFilters = without(this.all(), filter);
return this.save();
};
if ($scope.indexPattern.sourceFilters) {
$scope.rows = [];
each($scope.indexPattern.sourceFilters, (filter) => {
const matcher = fieldWildcardMatcher([ filter.value ]);
// compute which fields match a filter
const matches = $scope.indexPattern.getNonScriptedFields().map(f => f.name).filter(matcher).sort();
if ($scope.$parent.fieldFilter && !angularFilter(matches, $scope.$parent.fieldFilter).length) {
return;
}
// compute the rows
const rowScope = $scope.$new();
rowScope.filter = filter;
rowScopes.push(rowScope);
$scope.rows.push([
{
markup: filterHtml,
scope: rowScope
},
size(matches) ? escape(matches.join(', ')) : '<em>The source filter doesn\'t match any known fields.</em>',
{
markup: controlsHtml,
scope: rowScope
}
]);
});
// Update the tab count
find($scope.$parent.editSections, { index: 'sourceFilters' }).count = $scope.rows.length;
}
});
}
const confirmModalOptions = {
confirmButtonText: 'Delete filter',
onConfirm: doDelete
};
confirmModal(`Are you sure want to delete this filter?`, confirmModalOptions);
}
all() {
return this.$scope.indexPattern.sourceFilters || [];
}
delete(filter) {
const doDelete = () => {
if (this.editing === filter) {
this.editing = null;
}
this.$scope.indexPattern.sourceFilters = without(this.all(), filter);
create() {
const value = this.newValue;
this.newValue = null;
this.$scope.indexPattern.sourceFilters = [...this.all(), { value }];
return this.save();
};
}
const confirmModalOptions = {
confirmButtonText: 'Delete filter',
onConfirm: doDelete
};
confirmModal(`Are you sure want to delete this filter?`, confirmModalOptions);
save() {
this.saving = true;
this.$scope.indexPattern.save()
.then(() => this.editing = null)
.catch(notify.error)
.finally(() => this.saving = false);
}
}
create() {
const value = this.newValue;
this.newValue = null;
this.$scope.indexPattern.sourceFilters = [...this.all(), { value }];
return this.save();
}
save() {
this.saving = true;
this.$scope.indexPattern.save()
.then(() => this.editing = null)
.catch(notify.error)
.finally(() => this.saving = false);
}
}
};
});
};
});

View file

@ -21,44 +21,44 @@ const indexPatternsResolutions = {
// add a dependency to all of the subsection routes
uiRoutes
.defaults(/management\/kibana\/indices/, {
resolve: indexPatternsResolutions
});
.defaults(/management\/kibana\/indices/, {
resolve: indexPatternsResolutions
});
uiRoutes
.defaults(/management\/kibana\/index/, {
resolve: indexPatternsResolutions
});
.defaults(/management\/kibana\/index/, {
resolve: indexPatternsResolutions
});
// wrapper directive, which sets some global stuff up like the left nav
uiModules.get('apps/management')
.directive('kbnManagementIndices', function ($route, config, kbnUrl) {
return {
restrict: 'E',
transclude: true,
template: indexTemplate,
link: function ($scope) {
$scope.editingId = $route.current.params.indexPatternId;
config.bindToScope($scope, 'defaultIndex');
.directive('kbnManagementIndices', function ($route, config, kbnUrl) {
return {
restrict: 'E',
transclude: true,
template: indexTemplate,
link: function ($scope) {
$scope.editingId = $route.current.params.indexPatternId;
config.bindToScope($scope, 'defaultIndex');
$scope.$watch('defaultIndex', function () {
$scope.indexPatternList = $route.current.locals.indexPatterns.map(pattern => {
const id = pattern.id;
$scope.$watch('defaultIndex', function () {
$scope.indexPatternList = $route.current.locals.indexPatterns.map(pattern => {
const id = pattern.id;
return {
id: id,
title: pattern.get('title'),
url: kbnUrl.eval('#/management/kibana/indices/{{id}}', { id: id }),
class: 'sidebar-item-title ' + ($scope.editingId === id ? 'active' : ''),
default: $scope.defaultIndex === id
};
return {
id: id,
title: pattern.get('title'),
url: kbnUrl.eval('#/management/kibana/indices/{{id}}', { id: id }),
class: 'sidebar-item-title ' + ($scope.editingId === id ? 'active' : ''),
default: $scope.defaultIndex === id
};
});
});
});
$scope.$emit('application.load');
}
};
});
$scope.$emit('application.load');
}
};
});
management.getSection('kibana').register('indices', {
display: 'Index Patterns',

View file

@ -23,300 +23,300 @@ const indexPatternsResolutions = {
};
uiRoutes
.when('/management/kibana/objects', {
template: objectIndexHTML,
resolve: indexPatternsResolutions
});
.when('/management/kibana/objects', {
template: objectIndexHTML,
resolve: indexPatternsResolutions
});
uiRoutes
.when('/management/kibana/objects/:service', {
redirectTo: '/management/kibana/objects'
});
.when('/management/kibana/objects/:service', {
redirectTo: '/management/kibana/objects'
});
uiModules.get('apps/management')
.directive('kbnManagementObjects', function ($route, kbnIndex, Notifier, Private, kbnUrl, Promise, confirmModal) {
const savedObjectsClient = Private(SavedObjectsClientProvider);
.directive('kbnManagementObjects', function ($route, kbnIndex, Notifier, Private, kbnUrl, Promise, confirmModal) {
const savedObjectsClient = Private(SavedObjectsClientProvider);
return {
restrict: 'E',
controllerAs: 'managementObjectsController',
controller: function ($scope, $injector, $q, AppState) {
const notify = new Notifier({ location: 'Saved Objects' });
return {
restrict: 'E',
controllerAs: 'managementObjectsController',
controller: function ($scope, $injector, $q, AppState) {
const notify = new Notifier({ location: 'Saved Objects' });
// TODO: Migrate all scope variables to the controller.
const $state = $scope.state = new AppState();
$scope.currentTab = null;
$scope.selectedItems = [];
// TODO: Migrate all scope variables to the controller.
const $state = $scope.state = new AppState();
$scope.currentTab = null;
$scope.selectedItems = [];
this.areAllRowsChecked = function areAllRowsChecked() {
if ($scope.currentTab.data.length === 0) {
return false;
}
return $scope.selectedItems.length === $scope.currentTab.data.length;
};
const getData = function (filter) {
const services = savedObjectManagementRegistry.all().map(function (obj) {
const service = $injector.get(obj.service);
return service.findAll(filter).then(function (data) {
return {
service: service,
serviceName: obj.service,
title: obj.title,
type: service.type,
data: data.hits,
total: data.total
};
});
});
$q.all(services).then(function (data) {
$scope.services = sortBy(data, 'title');
if ($state.tab) $scope.currentTab = find($scope.services, { title: $state.tab });
$scope.$watch('state.tab', function (tab) {
if (!tab) $scope.changeTab($scope.services[0]);
});
});
};
const refreshData = () => {
return getData(this.advancedFilter);
};
// TODO: Migrate all scope methods to the controller.
$scope.toggleAll = function () {
if ($scope.selectedItems.length === $scope.currentTab.data.length) {
$scope.selectedItems.length = 0;
} else {
$scope.selectedItems = [].concat($scope.currentTab.data);
}
};
// TODO: Migrate all scope methods to the controller.
$scope.toggleItem = function (item) {
const i = $scope.selectedItems.indexOf(item);
if (i >= 0) {
$scope.selectedItems.splice(i, 1);
} else {
$scope.selectedItems.push(item);
}
};
// TODO: Migrate all scope methods to the controller.
$scope.open = function (item) {
kbnUrl.change(item.url.substr(1));
};
// TODO: Migrate all scope methods to the controller.
$scope.edit = function (service, item) {
const params = {
service: service.serviceName,
id: item.id
this.areAllRowsChecked = function areAllRowsChecked() {
if ($scope.currentTab.data.length === 0) {
return false;
}
return $scope.selectedItems.length === $scope.currentTab.data.length;
};
kbnUrl.change('/management/kibana/objects/{{ service }}/{{ id }}', params);
};
// TODO: Migrate all scope methods to the controller.
$scope.bulkDelete = function () {
function doBulkDelete() {
$scope.currentTab.service.delete(pluck($scope.selectedItems, 'id'))
.then(refreshData)
.then(function () {
$scope.selectedItems.length = 0;
})
.catch(error => notify.error(error));
}
const confirmModalOptions = {
confirmButtonText: `Delete ${$scope.currentTab.title}`,
onConfirm: doBulkDelete
};
confirmModal(
`Are you sure you want to delete the selected ${$scope.currentTab.title}? This action is irreversible!`,
confirmModalOptions
);
};
// TODO: Migrate all scope methods to the controller.
$scope.bulkExport = function () {
const objs = $scope.selectedItems.map(item => {
return { type: $scope.currentTab.type, id: item.id };
});
retrieveAndExportDocs(objs);
};
// TODO: Migrate all scope methods to the controller.
$scope.exportAll = () => Promise
.map($scope.services, service => service.service
.scanAll('')
.then(result => result.hits)
)
.then(results => saveToFile(flattenDeep(results)))
.catch(error => notify.error(error));
function retrieveAndExportDocs(objs) {
if (!objs.length) return notify.error('No saved objects to export.');
savedObjectsClient.bulkGet(objs)
.then(function (response) {
saveToFile(response.savedObjects.map(obj => {
const getData = function (filter) {
const services = savedObjectManagementRegistry.all().map(function (obj) {
const service = $injector.get(obj.service);
return service.findAll(filter).then(function (data) {
return {
_id: obj.id,
_type: obj.type,
_source: obj.attributes
service: service,
serviceName: obj.service,
title: obj.title,
type: service.type,
data: data.hits,
total: data.total
};
}));
});
});
}
function saveToFile(results) {
const blob = new Blob([angular.toJson(results, true)], { type: 'application/json' });
saveAs(blob, 'export.json');
}
$q.all(services).then(function (data) {
$scope.services = sortBy(data, 'title');
if ($state.tab) $scope.currentTab = find($scope.services, { title: $state.tab });
// TODO: Migrate all scope methods to the controller.
$scope.importAll = function (fileContents) {
let docs;
try {
docs = JSON.parse(fileContents);
} catch (e) {
notify.error('The file could not be processed.');
return;
}
$scope.$watch('state.tab', function (tab) {
if (!tab) $scope.changeTab($scope.services[0]);
});
});
};
// make sure we have an array, show an error otherwise
if (!Array.isArray(docs)) {
notify.error('Saved objects file format is invalid and cannot be imported.');
return;
}
const refreshData = () => {
return getData(this.advancedFilter);
};
return new Promise((resolve) => {
// TODO: Migrate all scope methods to the controller.
$scope.toggleAll = function () {
if ($scope.selectedItems.length === $scope.currentTab.data.length) {
$scope.selectedItems.length = 0;
} else {
$scope.selectedItems = [].concat($scope.currentTab.data);
}
};
// TODO: Migrate all scope methods to the controller.
$scope.toggleItem = function (item) {
const i = $scope.selectedItems.indexOf(item);
if (i >= 0) {
$scope.selectedItems.splice(i, 1);
} else {
$scope.selectedItems.push(item);
}
};
// TODO: Migrate all scope methods to the controller.
$scope.open = function (item) {
kbnUrl.change(item.url.substr(1));
};
// TODO: Migrate all scope methods to the controller.
$scope.edit = function (service, item) {
const params = {
service: service.serviceName,
id: item.id
};
kbnUrl.change('/management/kibana/objects/{{ service }}/{{ id }}', params);
};
// TODO: Migrate all scope methods to the controller.
$scope.bulkDelete = function () {
function doBulkDelete() {
$scope.currentTab.service.delete(pluck($scope.selectedItems, 'id'))
.then(refreshData)
.then(function () {
$scope.selectedItems.length = 0;
})
.catch(error => notify.error(error));
}
const confirmModalOptions = {
confirmButtonText: `Delete ${$scope.currentTab.title}`,
onConfirm: doBulkDelete
};
confirmModal(
`If any of the objects already exist, do you want to automatically overwrite them?`, {
confirmButtonText: `Yes, overwrite all`,
cancelButtonText: `No, prompt me for each one`,
onConfirm: () => resolve(true),
onCancel: () => resolve(false),
}
`Are you sure you want to delete the selected ${$scope.currentTab.title}? This action is irreversible!`,
confirmModalOptions
);
})
.then((overwriteAll) => {
// Keep a record of the index patterns assigned to our imported saved objects that do not
// exist. We will provide a way for the user to manually select a new index pattern for those
// saved objects.
const conflictedIndexPatterns = [];
// We want to do the same for saved searches, but we want to keep them separate because they need
// to be applied _first_ because other saved objects can be depedent on those saved searches existing
const conflictedSearchDocs = [];
};
function importDocument(swallowErrors, doc) {
const { service } = find($scope.services, { type: doc._type }) || {};
// TODO: Migrate all scope methods to the controller.
$scope.bulkExport = function () {
const objs = $scope.selectedItems.map(item => {
return { type: $scope.currentTab.type, id: item.id };
});
if (!service) {
const msg = `Skipped import of "${doc._source.title}" (${doc._id})`;
const reason = `Invalid type: "${doc._type}"`;
retrieveAndExportDocs(objs);
};
notify.warning(`${msg}, ${reason}`, {
lifetime: 0,
});
// TODO: Migrate all scope methods to the controller.
$scope.exportAll = () => Promise
.map($scope.services, service => service.service
.scanAll('')
.then(result => result.hits)
)
.then(results => saveToFile(flattenDeep(results)))
.catch(error => notify.error(error));
return;
}
function retrieveAndExportDocs(objs) {
if (!objs.length) return notify.error('No saved objects to export.');
return service.get()
.then(function (obj) {
obj.id = doc._id;
return obj.applyESResp(doc)
.then(() => {
return obj.save({ confirmOverwrite: !overwriteAll });
})
.catch((err) => {
if (swallowErrors && err instanceof SavedObjectNotFound) {
switch (err.savedObjectType) {
case 'search':
conflictedSearchDocs.push(doc);
return;
case 'index-pattern':
conflictedIndexPatterns.push({ obj, doc });
return;
}
}
// swallow errors here so that the remaining promise chain executes
err.message = `Importing ${obj.title} (${obj.id}) failed: ${err.message}`;
notify.error(err);
savedObjectsClient.bulkGet(objs)
.then(function (response) {
saveToFile(response.savedObjects.map(obj => {
return {
_id: obj.id,
_type: obj.type,
_source: obj.attributes
};
}));
});
}
function saveToFile(results) {
const blob = new Blob([angular.toJson(results, true)], { type: 'application/json' });
saveAs(blob, 'export.json');
}
// TODO: Migrate all scope methods to the controller.
$scope.importAll = function (fileContents) {
let docs;
try {
docs = JSON.parse(fileContents);
} catch (e) {
notify.error('The file could not be processed.');
return;
}
// make sure we have an array, show an error otherwise
if (!Array.isArray(docs)) {
notify.error('Saved objects file format is invalid and cannot be imported.');
return;
}
return new Promise((resolve) => {
confirmModal(
`If any of the objects already exist, do you want to automatically overwrite them?`, {
confirmButtonText: `Yes, overwrite all`,
cancelButtonText: `No, prompt me for each one`,
onConfirm: () => resolve(true),
onCancel: () => resolve(false),
}
);
})
.then((overwriteAll) => {
// Keep a record of the index patterns assigned to our imported saved objects that do not
// exist. We will provide a way for the user to manually select a new index pattern for those
// saved objects.
const conflictedIndexPatterns = [];
// We want to do the same for saved searches, but we want to keep them separate because they need
// to be applied _first_ because other saved objects can be depedent on those saved searches existing
const conflictedSearchDocs = [];
function importDocument(swallowErrors, doc) {
const { service } = find($scope.services, { type: doc._type }) || {};
if (!service) {
const msg = `Skipped import of "${doc._source.title}" (${doc._id})`;
const reason = `Invalid type: "${doc._type}"`;
notify.warning(`${msg}, ${reason}`, {
lifetime: 0,
});
});
}
function groupByType(docs) {
const defaultDocTypes = {
searches: [],
other: [],
};
return;
}
return docs.reduce((types, doc) => {
switch (doc._type) {
case 'search':
types.searches.push(doc);
break;
default:
types.other.push(doc);
return service.get()
.then(function (obj) {
obj.id = doc._id;
return obj.applyESResp(doc)
.then(() => {
return obj.save({ confirmOverwrite: !overwriteAll });
})
.catch((err) => {
if (swallowErrors && err instanceof SavedObjectNotFound) {
switch (err.savedObjectType) {
case 'search':
conflictedSearchDocs.push(doc);
return;
case 'index-pattern':
conflictedIndexPatterns.push({ obj, doc });
return;
}
}
// swallow errors here so that the remaining promise chain executes
err.message = `Importing ${obj.title} (${obj.id}) failed: ${err.message}`;
notify.error(err);
});
});
}
return types;
}, defaultDocTypes);
}
function resolveConflicts(objs, { obj }) {
const oldIndexId = obj.searchSource.getOwn('index');
const newIndexId = objs.find(({ oldId }) => oldId === oldIndexId).newId;
// If the user did not select a new index pattern in the modal, the id
// will be same as before, so don't try to update it
if (newIndexId === oldIndexId) {
return;
}
return obj.hydrateIndexPattern(newIndexId)
.then(() => obj.save({ confirmOverwrite: !overwriteAll }));
}
function groupByType(docs) {
const defaultDocTypes = {
searches: [],
other: [],
};
const docTypes = groupByType(docs);
return Promise.map(docTypes.searches, importDocument.bind(null, true))
.then(() => Promise.map(docTypes.other, importDocument.bind(null, true)))
.then(() => {
if (conflictedIndexPatterns.length) {
return new Promise((resolve, reject) => {
showChangeIndexModal(
(objs) => {
Promise.map(conflictedIndexPatterns, resolveConflicts.bind(null, objs))
.then(resolve)
.catch(reject);
},
conflictedIndexPatterns,
$route.current.locals.indexPatterns,
);
});
return docs.reduce((types, doc) => {
switch (doc._type) {
case 'search':
types.searches.push(doc);
break;
default:
types.other.push(doc);
}
return types;
}, defaultDocTypes);
}
})
.then(() => Promise.map(conflictedSearchDocs, importDocument.bind(null, false)))
.then(refreshData)
.catch(notify.error);
function resolveConflicts(objs, { obj }) {
const oldIndexId = obj.searchSource.getOwn('index');
const newIndexId = objs.find(({ oldId }) => oldId === oldIndexId).newId;
// If the user did not select a new index pattern in the modal, the id
// will be same as before, so don't try to update it
if (newIndexId === oldIndexId) {
return;
}
return obj.hydrateIndexPattern(newIndexId)
.then(() => obj.save({ confirmOverwrite: !overwriteAll }));
}
const docTypes = groupByType(docs);
return Promise.map(docTypes.searches, importDocument.bind(null, true))
.then(() => Promise.map(docTypes.other, importDocument.bind(null, true)))
.then(() => {
if (conflictedIndexPatterns.length) {
return new Promise((resolve, reject) => {
showChangeIndexModal(
(objs) => {
Promise.map(conflictedIndexPatterns, resolveConflicts.bind(null, objs))
.then(resolve)
.catch(reject);
},
conflictedIndexPatterns,
$route.current.locals.indexPatterns,
);
});
}
})
.then(() => Promise.map(conflictedSearchDocs, importDocument.bind(null, false)))
.then(refreshData)
.catch(notify.error);
});
};
// TODO: Migrate all scope methods to the controller.
$scope.changeTab = function (tab) {
$scope.currentTab = tab;
$scope.selectedItems.length = 0;
$state.tab = tab.title;
$state.save();
};
$scope.$watch('managementObjectsController.advancedFilter', function (filter) {
getData(filter);
});
};
// TODO: Migrate all scope methods to the controller.
$scope.changeTab = function (tab) {
$scope.currentTab = tab;
$scope.selectedItems.length = 0;
$state.tab = tab.title;
$state.save();
};
$scope.$watch('managementObjectsController.advancedFilter', function (filter) {
getData(filter);
});
}
};
});
}
};
});

View file

@ -10,21 +10,21 @@ import { castEsToKbnFieldTypeName } from '../../../../../../utils';
import { SavedObjectsClientProvider } from 'ui/saved_objects';
uiRoutes
.when('/management/kibana/objects/:service/:id', {
template: objectViewHTML
});
.when('/management/kibana/objects/:service/:id', {
template: objectViewHTML
});
uiModules.get('apps/management')
.directive('kbnManagementObjectsView', function (kbnIndex, Notifier, confirmModal) {
return {
restrict: 'E',
controller: function ($scope, $injector, $routeParams, $location, $window, $rootScope, Private) {
const notify = new Notifier({ location: 'SavedObject view' });
const serviceObj = savedObjectManagementRegistry.get($routeParams.service);
const service = $injector.get(serviceObj.service);
const savedObjectsClient = Private(SavedObjectsClientProvider);
.directive('kbnManagementObjectsView', function (kbnIndex, Notifier, confirmModal) {
return {
restrict: 'E',
controller: function ($scope, $injector, $routeParams, $location, $window, $rootScope, Private) {
const notify = new Notifier({ location: 'SavedObject view' });
const serviceObj = savedObjectManagementRegistry.get($routeParams.service);
const service = $injector.get(serviceObj.service);
const savedObjectsClient = Private(SavedObjectsClientProvider);
/**
/**
* Creates a field definition and pushes it to the memo stack. This function
* is designed to be used in conjunction with _.reduce(). If the
* values is plain object it will recurse through all the keys till it hits
@ -37,188 +37,188 @@ uiModules.get('apps/management')
* @param {array} parents The parent keys to the field
* @returns {array}
*/
const createField = function (memo, val, key, collection, parents) {
if (Array.isArray(parents)) {
parents.push(key);
} else {
parents = [key];
}
const field = { type: 'text', name: parents.join('.'), value: val };
if (_.isString(field.value)) {
try {
field.value = angular.toJson(JSON.parse(field.value), true);
field.type = 'json';
} catch (err) {
field.value = field.value;
}
} else if (_.isNumeric(field.value)) {
field.type = 'number';
} else if (Array.isArray(field.value)) {
field.type = 'array';
field.value = angular.toJson(field.value, true);
} else if (_.isBoolean(field.value)) {
field.type = 'boolean';
field.value = field.value;
} else if (_.isPlainObject(field.value)) {
// do something recursive
return _.reduce(field.value, _.partialRight(createField, parents), memo);
}
memo.push(field);
// once the field is added to the object you need to pop the parents
// to remove it since we've hit the end of the branch.
parents.pop();
return memo;
};
const readObjectClass = function (fields, Class) {
const fieldMap = _.indexBy(fields, 'name');
_.forOwn(Class.mapping, function (esType, name) {
if (fieldMap[name]) return;
fields.push({
name: name,
type: (function () {
switch (castEsToKbnFieldTypeName(esType)) {
case 'string': return 'text';
case 'number': return 'number';
case 'boolean': return 'boolean';
default: return 'json';
}
}())
});
});
if (Class.searchSource && !fieldMap['kibanaSavedObjectMeta.searchSourceJSON']) {
fields.push({
name: 'kibanaSavedObjectMeta.searchSourceJSON',
type: 'json',
value: '{}'
});
}
};
$scope.notFound = $routeParams.notFound;
$scope.title = service.type;
savedObjectsClient.get(service.type, $routeParams.id)
.then(function (obj) {
$scope.obj = obj;
$scope.link = service.urlFor(obj.id);
const fields = _.reduce(obj.attributes, createField, []);
if (service.Class) readObjectClass(fields, service.Class);
// sorts twice since we want numerical sort to prioritize over name,
// and sortBy will do string comparison if trying to match against strings
const nameSortedFields = _.sortBy(fields, 'name');
$scope.fields = _.sortBy(nameSortedFields, (field) => {
const orderIndex = service.Class.fieldOrder ? service.Class.fieldOrder.indexOf(field.name) : -1;
return (orderIndex > -1) ? orderIndex : Infinity;
});
})
.catch(notify.fatal);
// This handles the validation of the Ace Editor. Since we don't have any
// other hooks into the editors to tell us if the content is valid or not
// we need to use the annotations to see if they have any errors. If they
// do then we push the field.name to aceInvalidEditor variable.
// Otherwise we remove it.
const loadedEditors = [];
$scope.aceInvalidEditors = [];
$scope.aceLoaded = function (editor) {
if (_.contains(loadedEditors, editor)) return;
loadedEditors.push(editor);
editor.$blockScrolling = Infinity;
const session = editor.getSession();
const fieldName = editor.container.id;
session.setTabSize(2);
session.setUseSoftTabs(true);
session.on('changeAnnotation', function () {
const annotations = session.getAnnotations();
if (_.some(annotations, { type: 'error' })) {
if (!_.contains($scope.aceInvalidEditors, fieldName)) {
$scope.aceInvalidEditors.push(fieldName);
}
const createField = function (memo, val, key, collection, parents) {
if (Array.isArray(parents)) {
parents.push(key);
} else {
$scope.aceInvalidEditors = _.without($scope.aceInvalidEditors, fieldName);
parents = [key];
}
if (!$rootScope.$$phase) $scope.$apply();
});
};
const field = { type: 'text', name: parents.join('.'), value: val };
$scope.cancel = function () {
$window.history.back();
return false;
};
if (_.isString(field.value)) {
try {
field.value = angular.toJson(JSON.parse(field.value), true);
field.type = 'json';
} catch (err) {
field.value = field.value;
}
} else if (_.isNumeric(field.value)) {
field.type = 'number';
} else if (Array.isArray(field.value)) {
field.type = 'array';
field.value = angular.toJson(field.value, true);
} else if (_.isBoolean(field.value)) {
field.type = 'boolean';
field.value = field.value;
} else if (_.isPlainObject(field.value)) {
// do something recursive
return _.reduce(field.value, _.partialRight(createField, parents), memo);
}
/**
memo.push(field);
// once the field is added to the object you need to pop the parents
// to remove it since we've hit the end of the branch.
parents.pop();
return memo;
};
const readObjectClass = function (fields, Class) {
const fieldMap = _.indexBy(fields, 'name');
_.forOwn(Class.mapping, function (esType, name) {
if (fieldMap[name]) return;
fields.push({
name: name,
type: (function () {
switch (castEsToKbnFieldTypeName(esType)) {
case 'string': return 'text';
case 'number': return 'number';
case 'boolean': return 'boolean';
default: return 'json';
}
}())
});
});
if (Class.searchSource && !fieldMap['kibanaSavedObjectMeta.searchSourceJSON']) {
fields.push({
name: 'kibanaSavedObjectMeta.searchSourceJSON',
type: 'json',
value: '{}'
});
}
};
$scope.notFound = $routeParams.notFound;
$scope.title = service.type;
savedObjectsClient.get(service.type, $routeParams.id)
.then(function (obj) {
$scope.obj = obj;
$scope.link = service.urlFor(obj.id);
const fields = _.reduce(obj.attributes, createField, []);
if (service.Class) readObjectClass(fields, service.Class);
// sorts twice since we want numerical sort to prioritize over name,
// and sortBy will do string comparison if trying to match against strings
const nameSortedFields = _.sortBy(fields, 'name');
$scope.fields = _.sortBy(nameSortedFields, (field) => {
const orderIndex = service.Class.fieldOrder ? service.Class.fieldOrder.indexOf(field.name) : -1;
return (orderIndex > -1) ? orderIndex : Infinity;
});
})
.catch(notify.fatal);
// This handles the validation of the Ace Editor. Since we don't have any
// other hooks into the editors to tell us if the content is valid or not
// we need to use the annotations to see if they have any errors. If they
// do then we push the field.name to aceInvalidEditor variable.
// Otherwise we remove it.
const loadedEditors = [];
$scope.aceInvalidEditors = [];
$scope.aceLoaded = function (editor) {
if (_.contains(loadedEditors, editor)) return;
loadedEditors.push(editor);
editor.$blockScrolling = Infinity;
const session = editor.getSession();
const fieldName = editor.container.id;
session.setTabSize(2);
session.setUseSoftTabs(true);
session.on('changeAnnotation', function () {
const annotations = session.getAnnotations();
if (_.some(annotations, { type: 'error' })) {
if (!_.contains($scope.aceInvalidEditors, fieldName)) {
$scope.aceInvalidEditors.push(fieldName);
}
} else {
$scope.aceInvalidEditors = _.without($scope.aceInvalidEditors, fieldName);
}
if (!$rootScope.$$phase) $scope.$apply();
});
};
$scope.cancel = function () {
$window.history.back();
return false;
};
/**
* Deletes an object and sets the notification
* @param {type} name description
* @returns {type} description
*/
$scope.delete = function () {
function doDelete() {
savedObjectsClient.delete(service.type, $routeParams.id)
$scope.delete = function () {
function doDelete() {
savedObjectsClient.delete(service.type, $routeParams.id)
.then(function () {
return redirectHandler('deleted');
})
.catch(notify.fatal);
}
const confirmModalOptions = {
onConfirm: doDelete,
confirmButtonText: 'Delete object'
};
confirmModal(
'Are you sure want to delete this object? This action is irreversible!',
confirmModalOptions
);
};
$scope.submit = function () {
const source = _.cloneDeep($scope.obj.attributes);
_.each($scope.fields, function (field) {
let value = field.value;
if (field.type === 'number') {
value = Number(field.value);
}
if (field.type === 'array') {
value = JSON.parse(field.value);
}
_.set(source, field.name, value);
});
savedObjectsClient.update(service.type, $routeParams.id, source)
.then(function () {
return redirectHandler('deleted');
return redirectHandler('updated');
})
.catch(notify.fatal);
}
const confirmModalOptions = {
onConfirm: doDelete,
confirmButtonText: 'Delete object'
};
confirmModal(
'Are you sure want to delete this object? This action is irreversible!',
confirmModalOptions
);
};
$scope.submit = function () {
const source = _.cloneDeep($scope.obj.attributes);
function redirectHandler(action) {
const msg = 'You successfully ' + action + ' the "' + $scope.obj.attributes.title + '" ' + $scope.title.toLowerCase() + ' object';
_.each($scope.fields, function (field) {
let value = field.value;
if (field.type === 'number') {
value = Number(field.value);
}
if (field.type === 'array') {
value = JSON.parse(field.value);
}
_.set(source, field.name, value);
});
savedObjectsClient.update(service.type, $routeParams.id, source)
.then(function () {
return redirectHandler('updated');
})
.catch(notify.fatal);
};
function redirectHandler(action) {
const msg = 'You successfully ' + action + ' the "' + $scope.obj.attributes.title + '" ' + $scope.title.toLowerCase() + ' object';
$location.path('/management/kibana/objects').search({
_a: rison.encode({
tab: serviceObj.title
})
});
notify.info(msg);
$location.path('/management/kibana/objects').search({
_a: rison.encode({
tab: serviceObj.title
})
});
notify.info(msg);
}
}
}
};
});
};
});

View file

@ -158,7 +158,7 @@ export class ChangeIndexModal extends React.Component {
Please select the index patterns you&apos;d like re-associated them with.
</p>
{ totalIndexPatterns > perPage
?
? (
<KuiControlledTable className="kuiVerticalRhythm">
<KuiToolBar>
<KuiToolBarSection>
@ -175,8 +175,9 @@ export class ChangeIndexModal extends React.Component {
</KuiToolBar>
<TableComponent/>
</KuiControlledTable>
:
) : (
<TableComponent/>
)
}
</KuiModalBody>

View file

@ -5,72 +5,72 @@ import { keyCodes } from 'ui_framework/services';
import advancedRowTemplate from 'plugins/kibana/management/sections/settings/advanced_row.html';
uiModules.get('apps/management')
.directive('advancedRow', function (config, Notifier) {
return {
restrict: 'A',
replace: true,
template: advancedRowTemplate,
scope: {
conf: '=advancedRow',
configs: '='
},
link: function ($scope) {
const notify = new Notifier();
.directive('advancedRow', function (config, Notifier) {
return {
restrict: 'A',
replace: true,
template: advancedRowTemplate,
scope: {
conf: '=advancedRow',
configs: '='
},
link: function ($scope) {
const notify = new Notifier();
// To allow passing form validation state back
$scope.forms = {};
// To allow passing form validation state back
$scope.forms = {};
// setup loading flag, run async op, then clear loading and editing flag (just in case)
const loading = function (conf, fn) {
conf.loading = true;
fn()
.then(function () {
conf.loading = conf.editing = false;
})
.catch(notify.fatal);
};
// setup loading flag, run async op, then clear loading and editing flag (just in case)
const loading = function (conf, fn) {
conf.loading = true;
fn()
.then(function () {
conf.loading = conf.editing = false;
})
.catch(notify.fatal);
};
$scope.maybeCancel = function ($event, conf) {
if ($event.keyCode === keyCodes.ESCAPE) {
$scope.cancelEdit(conf);
}
};
$scope.edit = function (conf) {
conf.unsavedValue = conf.value == null ? conf.defVal : conf.value;
$scope.configs.forEach(function (c) {
c.editing = (c === conf);
});
};
$scope.save = function (conf) {
loading(conf, function () {
if (conf.unsavedValue === conf.defVal) {
return config.remove(conf.name);
$scope.maybeCancel = function ($event, conf) {
if ($event.keyCode === keyCodes.ESCAPE) {
$scope.cancelEdit(conf);
}
};
return config.set(conf.name, conf.unsavedValue);
});
};
$scope.edit = function (conf) {
conf.unsavedValue = conf.value == null ? conf.defVal : conf.value;
$scope.configs.forEach(function (c) {
c.editing = (c === conf);
});
};
$scope.cancelEdit = function (conf) {
conf.editing = false;
};
$scope.save = function (conf) {
loading(conf, function () {
if (conf.unsavedValue === conf.defVal) {
return config.remove(conf.name);
}
$scope.clear = function (conf) {
return loading(conf, function () {
return config.remove(conf.name);
});
};
return config.set(conf.name, conf.unsavedValue);
});
};
$scope.isDefaultValue = (conf) => {
$scope.cancelEdit = function (conf) {
conf.editing = false;
};
$scope.clear = function (conf) {
return loading(conf, function () {
return config.remove(conf.name);
});
};
$scope.isDefaultValue = (conf) => {
// conf.isCustom = custom setting, provided by user, so there is no notion of
// having a default or non-default value for it
return conf.isCustom
return conf.isCustom
|| conf.value === undefined
|| conf.value === ''
|| String(conf.value) === String(conf.defVal);
};
}
};
});
};
}
};
});

View file

@ -8,37 +8,37 @@ import indexTemplate from 'plugins/kibana/management/sections/settings/index.htm
import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
uiRoutes
.when('/management/kibana/settings', {
template: indexTemplate
});
.when('/management/kibana/settings', {
template: indexTemplate
});
uiModules.get('apps/management')
.directive('kbnManagementAdvanced', function (config) {
return {
restrict: 'E',
link: function ($scope) {
.directive('kbnManagementAdvanced', function (config) {
return {
restrict: 'E',
link: function ($scope) {
// react to changes of the config values
config.watchAll(changed, $scope);
config.watchAll(changed, $scope);
// initial config setup
changed();
// initial config setup
changed();
function changed() {
const all = config.getAll();
const editable = _(all)
.map((def, name) => toEditableConfig({
def,
name,
value: def.userValue,
isCustom: config.isCustom(name)
}))
.value();
const writable = _.reject(editable, 'readonly');
$scope.configs = writable;
function changed() {
const all = config.getAll();
const editable = _(all)
.map((def, name) => toEditableConfig({
def,
name,
value: def.userValue,
isCustom: config.isCustom(name)
}))
.value();
const writable = _.reject(editable, 'readonly');
$scope.configs = writable;
}
}
}
};
});
};
});
management.getSection('kibana').register('settings', {
display: 'Advanced Settings',

View file

@ -2,7 +2,7 @@ import { propFilter } from 'ui/filters/_prop_filter';
import { uiModules } from 'ui/modules';
uiModules
.get('kibana')
.filter('aggFilter', function () {
return propFilter('name');
});
.get('kibana')
.filter('aggFilter', function () {
return propFilter('name');
});

View file

@ -23,52 +23,52 @@ import { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url';
import { migrateLegacyQuery } from 'ui/utils/migrateLegacyQuery';
uiRoutes
.when(VisualizeConstants.CREATE_PATH, {
template: editorTemplate,
resolve: {
savedVis: function (savedVisualizations, courier, $route, Private) {
const visTypes = Private(VisTypesRegistryProvider);
const visType = _.find(visTypes, { name: $route.current.params.type });
const shouldHaveIndex = visType.requiresSearch && visType.options.showIndexSelection;
const hasIndex = $route.current.params.indexPattern || $route.current.params.savedSearchId;
if (shouldHaveIndex && !hasIndex) {
throw new Error('You must provide either an indexPattern or a savedSearchId');
}
.when(VisualizeConstants.CREATE_PATH, {
template: editorTemplate,
resolve: {
savedVis: function (savedVisualizations, courier, $route, Private) {
const visTypes = Private(VisTypesRegistryProvider);
const visType = _.find(visTypes, { name: $route.current.params.type });
const shouldHaveIndex = visType.requiresSearch && visType.options.showIndexSelection;
const hasIndex = $route.current.params.indexPattern || $route.current.params.savedSearchId;
if (shouldHaveIndex && !hasIndex) {
throw new Error('You must provide either an indexPattern or a savedSearchId');
}
return savedVisualizations.get($route.current.params)
.catch(courier.redirectWhenMissing({
'*': '/visualize'
}));
return savedVisualizations.get($route.current.params)
.catch(courier.redirectWhenMissing({
'*': '/visualize'
}));
}
}
}
})
.when(`${VisualizeConstants.EDIT_PATH}/:id`, {
template: editorTemplate,
resolve: {
savedVis: function (savedVisualizations, courier, $route) {
return savedVisualizations.get($route.current.params.id)
.catch(courier.redirectWhenMissing({
'visualization': '/visualize',
'search': '/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
'index-pattern': '/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
'index-pattern-field': '/management/kibana/objects/savedVisualizations/' + $route.current.params.id
}));
})
.when(`${VisualizeConstants.EDIT_PATH}/:id`, {
template: editorTemplate,
resolve: {
savedVis: function (savedVisualizations, courier, $route) {
return savedVisualizations.get($route.current.params.id)
.catch(courier.redirectWhenMissing({
'visualization': '/visualize',
'search': '/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
'index-pattern': '/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
'index-pattern-field': '/management/kibana/objects/savedVisualizations/' + $route.current.params.id
}));
}
}
}
});
});
uiModules
.get('app/visualize', [
'kibana/notify',
'kibana/courier'
])
.directive('visualizeApp', function () {
return {
restrict: 'E',
controllerAs: 'visualizeApp',
controller: VisEditor,
};
});
.get('app/visualize', [
'kibana/notify',
'kibana/courier'
])
.directive('visualizeApp', function () {
return {
restrict: 'E',
controllerAs: 'visualizeApp',
controller: VisEditor,
};
});
function VisEditor($scope, $route, timefilter, AppState, $window, kbnUrl, courier, Private, Promise, config, kbnBaseUrl) {
const docTitle = Private(DocTitleProvider);
@ -145,9 +145,9 @@ function VisEditor($scope, $route, timefilter, AppState, $window, kbnUrl, courie
Promise.try(function () {
vis.setState(appState.vis);
})
.catch(courier.redirectWhenMissing({
'index-pattern-field': '/visualize'
}));
.catch(courier.redirectWhenMissing({
'index-pattern-field': '/visualize'
}));
}
return appState;
@ -240,36 +240,36 @@ function VisEditor($scope, $route, timefilter, AppState, $window, kbnUrl, courie
savedVis.uiStateJSON = angular.toJson($scope.uiState.getChanges());
savedVis.save()
.then(function (id) {
stateMonitor.setInitialState($state.toJSON());
$scope.kbnTopNav.close('save');
.then(function (id) {
stateMonitor.setInitialState($state.toJSON());
$scope.kbnTopNav.close('save');
if (id) {
notify.info('Saved Visualization "' + savedVis.title + '"');
if ($scope.isAddToDashMode()) {
const savedVisualizationParsedUrl = new KibanaParsedUrl({
basePath: chrome.getBasePath(),
appId: kbnBaseUrl.slice('/app/'.length),
appPath: kbnUrl.eval(`${VisualizeConstants.EDIT_PATH}/{{id}}`, { id: savedVis.id }),
});
// Manually insert a new url so the back button will open the saved visualization.
$window.history.pushState({}, '', savedVisualizationParsedUrl.getRootRelativePath());
// Since we aren't reloading the page, only inserting a new browser history item, we need to manually update
// the last url for this app, so directly clicking on the Visualize tab will also bring the user to the saved
// url, not the unsaved one.
chrome.trackSubUrlForApp('kibana:visualize', savedVisualizationParsedUrl);
if (id) {
notify.info('Saved Visualization "' + savedVis.title + '"');
if ($scope.isAddToDashMode()) {
const savedVisualizationParsedUrl = new KibanaParsedUrl({
basePath: chrome.getBasePath(),
appId: kbnBaseUrl.slice('/app/'.length),
appPath: kbnUrl.eval(`${VisualizeConstants.EDIT_PATH}/{{id}}`, { id: savedVis.id }),
});
// Manually insert a new url so the back button will open the saved visualization.
$window.history.pushState({}, '', savedVisualizationParsedUrl.getRootRelativePath());
// Since we aren't reloading the page, only inserting a new browser history item, we need to manually update
// the last url for this app, so directly clicking on the Visualize tab will also bring the user to the saved
// url, not the unsaved one.
chrome.trackSubUrlForApp('kibana:visualize', savedVisualizationParsedUrl);
const lastDashboardAbsoluteUrl = chrome.getNavLinkById('kibana:dashboard').lastSubUrl;
const dashboardParsedUrl = absoluteToParsedUrl(lastDashboardAbsoluteUrl, chrome.getBasePath());
dashboardParsedUrl.addQueryParameter(DashboardConstants.NEW_VISUALIZATION_ID_PARAM, savedVis.id);
kbnUrl.change(dashboardParsedUrl.appPath);
} else if (savedVis.id === $route.current.params.id) {
docTitle.change(savedVis.lastSavedTitle);
} else {
kbnUrl.change(`${VisualizeConstants.EDIT_PATH}/{{id}}`, { id: savedVis.id });
const lastDashboardAbsoluteUrl = chrome.getNavLinkById('kibana:dashboard').lastSubUrl;
const dashboardParsedUrl = absoluteToParsedUrl(lastDashboardAbsoluteUrl, chrome.getBasePath());
dashboardParsedUrl.addQueryParameter(DashboardConstants.NEW_VISUALIZATION_ID_PARAM, savedVis.id);
kbnUrl.change(dashboardParsedUrl.appPath);
} else if (savedVis.id === $route.current.params.id) {
docTitle.change(savedVis.lastSavedTitle);
} else {
kbnUrl.change(`${VisualizeConstants.EDIT_PATH}/{{id}}`, { id: savedVis.id });
}
}
}
}, notify.error);
}, notify.error);
};
$scope.unlink = function () {
@ -286,11 +286,11 @@ function VisEditor($scope, $route, timefilter, AppState, $window, kbnUrl, courie
// copy over all state except "aggs", "query" and "filter"
_(parent.toJSON())
.omit(['aggs', 'filter', 'query'])
.forOwn(function (val, key) {
searchSource.set(key, val);
})
.commit();
.omit(['aggs', 'filter', 'query'])
.forOwn(function (val, key) {
searchSource.set(key, val);
})
.commit();
$state.query = searchSource.get('query');
$state.filters = searchSource.get('filter');

View file

@ -16,14 +16,14 @@ import { VisualizeConstants } from './visualize_constants';
import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
uiRoutes
.defaults(/visualize/, {
requireDefaultIndex: true
})
.when(VisualizeConstants.LANDING_PAGE_PATH, {
template: visualizeListingTemplate,
controller: VisualizeListingController,
controllerAs: 'listingController',
});
.defaults(/visualize/, {
requireDefaultIndex: true
})
.when(VisualizeConstants.LANDING_PAGE_PATH, {
template: visualizeListingTemplate,
controller: VisualizeListingController,
controllerAs: 'listingController',
});
FeatureCatalogueRegistryProvider.register(() => {
return {

View file

@ -11,131 +11,131 @@ import { VisProvider } from 'ui/vis';
import { uiModules } from 'ui/modules';
uiModules
.get('app/visualize')
.factory('SavedVis', function (config, $injector, courier, Promise, savedSearches, Private) {
const Vis = Private(VisProvider);
.get('app/visualize')
.factory('SavedVis', function (config, $injector, courier, Promise, savedSearches, Private) {
const Vis = Private(VisProvider);
_.class(SavedVis).inherits(courier.SavedObject);
function SavedVis(opts) {
const self = this;
opts = opts || {};
if (typeof opts !== 'object') opts = { id: opts };
_.class(SavedVis).inherits(courier.SavedObject);
function SavedVis(opts) {
const self = this;
opts = opts || {};
if (typeof opts !== 'object') opts = { id: opts };
SavedVis.Super.call(self, {
type: SavedVis.type,
mapping: SavedVis.mapping,
searchSource: SavedVis.searchSource,
SavedVis.Super.call(self, {
type: SavedVis.type,
mapping: SavedVis.mapping,
searchSource: SavedVis.searchSource,
id: opts.id,
indexPattern: opts.indexPattern,
defaults: {
title: 'New Visualization',
visState: (function () {
if (!opts.type) return null;
const def = {};
def.type = opts.type;
return def;
}()),
uiStateJSON: '{}',
description: '',
savedSearchId: opts.savedSearchId,
version: 1
},
id: opts.id,
indexPattern: opts.indexPattern,
defaults: {
title: 'New Visualization',
visState: (function () {
if (!opts.type) return null;
const def = {};
def.type = opts.type;
return def;
}()),
uiStateJSON: '{}',
description: '',
savedSearchId: opts.savedSearchId,
version: 1
},
afterESResp: this._afterEsResp
afterESResp: this._afterEsResp
});
}
SavedVis.type = 'visualization';
SavedVis.mapping = {
title: 'text',
visState: 'json',
uiStateJSON: 'text',
description: 'text',
savedSearchId: 'keyword',
version: 'integer'
};
// Order these fields to the top, the rest are alphabetical
SavedVis.fieldOrder = ['title', 'description'];
SavedVis.searchSource = true;
SavedVis.prototype._afterEsResp = function () {
const self = this;
return self._getLinkedSavedSearch()
.then(function () {
self.searchSource.size(0);
return self.vis ? self._updateVis() : self._createVis();
})
.then(function () {
self.searchSource.onRequestStart((searchSource, searchRequest) => {
return self.vis.onSearchRequestStart(searchSource, searchRequest);
});
self.searchSource.aggs(function () {
return self.vis.aggs.toDsl();
});
return self;
});
};
SavedVis.prototype._getLinkedSavedSearch = Promise.method(function () {
const self = this;
const linkedSearch = !!self.savedSearchId;
const current = self.savedSearch;
if (linkedSearch && current && current.id === self.savedSearchId) {
return;
}
if (self.savedSearch) {
self.searchSource.inherits(self.savedSearch.searchSource.getParent());
self.savedSearch.destroy();
self.savedSearch = null;
}
if (linkedSearch) {
return savedSearches.get(self.savedSearchId)
.then(function (savedSearch) {
self.savedSearch = savedSearch;
self.searchSource.inherits(self.savedSearch.searchSource);
});
}
});
}
SavedVis.type = 'visualization';
SavedVis.prototype._createVis = function () {
const self = this;
SavedVis.mapping = {
title: 'text',
visState: 'json',
uiStateJSON: 'text',
description: 'text',
savedSearchId: 'keyword',
version: 'integer'
};
if (self.stateJSON) {
self.visState = Vis.convertOldState(self.typeName, JSON.parse(self.stateJSON));
}
// Order these fields to the top, the rest are alphabetical
SavedVis.fieldOrder = ['title', 'description'];
// visState doesn't yet exist when importing a visualization, so we can't
// assume that exists at this point. If it does exist, then we're not
// importing a visualization, so we want to sync the title.
if (self.visState) {
self.visState.title = self.title;
}
self.vis = new Vis(
self.searchSource.get('index'),
self.visState
);
SavedVis.searchSource = true;
return self.vis;
};
SavedVis.prototype._afterEsResp = function () {
const self = this;
SavedVis.prototype._updateVis = function () {
const self = this;
return self._getLinkedSavedSearch()
.then(function () {
self.searchSource.size(0);
return self.vis ? self._updateVis() : self._createVis();
})
.then(function () {
self.searchSource.onRequestStart((searchSource, searchRequest) => {
return self.vis.onSearchRequestStart(searchSource, searchRequest);
});
self.searchSource.aggs(function () {
return self.vis.aggs.toDsl();
});
return self;
});
};
SavedVis.prototype._getLinkedSavedSearch = Promise.method(function () {
const self = this;
const linkedSearch = !!self.savedSearchId;
const current = self.savedSearch;
if (linkedSearch && current && current.id === self.savedSearchId) {
return;
}
if (self.savedSearch) {
self.searchSource.inherits(self.savedSearch.searchSource.getParent());
self.savedSearch.destroy();
self.savedSearch = null;
}
if (linkedSearch) {
return savedSearches.get(self.savedSearchId)
.then(function (savedSearch) {
self.savedSearch = savedSearch;
self.searchSource.inherits(self.savedSearch.searchSource);
});
}
});
SavedVis.prototype._createVis = function () {
const self = this;
if (self.stateJSON) {
self.visState = Vis.convertOldState(self.typeName, JSON.parse(self.stateJSON));
}
// visState doesn't yet exist when importing a visualization, so we can't
// assume that exists at this point. If it does exist, then we're not
// importing a visualization, so we want to sync the title.
if (self.visState) {
self.vis.indexPattern = self.searchSource.get('index');
self.visState.title = self.title;
}
self.vis = new Vis(
self.searchSource.get('index'),
self.visState
);
self.vis.setState(self.visState);
};
return self.vis;
};
SavedVis.prototype._updateVis = function () {
const self = this;
self.vis.indexPattern = self.searchSource.get('index');
self.visState.title = self.title;
self.vis.setState(self.visState);
};
return SavedVis;
});
return SavedVis;
});

View file

@ -143,8 +143,8 @@ module.controller('VisualizeWizardStep1', function ($scope, $route, kbnUrl, time
$scope.getVisTypeUrl = function (visType) {
const baseUrl =
visType.requiresSearch && visType.options.showIndexSelection
? `#${VisualizeConstants.WIZARD_STEP_2_PAGE_PATH}?`
: `#${VisualizeConstants.CREATE_PATH}?`;
? `#${VisualizeConstants.WIZARD_STEP_2_PAGE_PATH}?`
: `#${VisualizeConstants.CREATE_PATH}?`;
const params = [`type=${encodeURIComponent(visType.name)}`];

View file

@ -13,14 +13,14 @@ export default function registerCount(server) {
allowNoIndices: false,
index: req.params.id
})
.then(
function (res) {
reply({ count: res.count });
},
function (error) {
reply(handleESError(error));
}
);
.then(
function (res) {
reply({ count: res.count });
},
function (error) {
reply(handleESError(error));
}
);
}
});
}

View file

@ -39,20 +39,20 @@ function StandardAgg(props) {
</div>
{
model.type !== 'count'
? (
<div className="vis_editor__item">
<label className="vis_editor__label" htmlFor={htmlId('field')}>Field</label>
<FieldSelect
id={htmlId('field')}
fields={fields}
type={model.type}
restrict={restrict}
indexPattern={indexPattern}
value={model.field}
onChange={handleSelectChange('field')}
/>
</div>
) : null
? (
<div className="vis_editor__item">
<label className="vis_editor__label" htmlFor={htmlId('field')}>Field</label>
<FieldSelect
id={htmlId('field')}
fields={fields}
type={model.type}
restrict={restrict}
indexPattern={indexPattern}
value={model.field}
onChange={handleSelectChange('field')}
/>
</div>
) : null
}
</AggRow>
);

View file

@ -81,18 +81,18 @@ class ColorPicker extends Component {
{ clear }
{
this.state.displayPicker
? (
<div className="vis_editor__color_picker-popover">
<div
className="vis_editor__color_picker-cover"
onClick={this.handleClose}
/>
<Picker
color={value}
onChangeComplete={this.handleChange}
/>
</div>
) : null
? (
<div className="vis_editor__color_picker-popover">
<div
className="vis_editor__color_picker-cover"
onClick={this.handleClose}
/>
<Picker
color={value}
onChangeComplete={this.handleChange}
/>
</div>
) : null
}
</div>
);

View file

@ -58,7 +58,7 @@ export default function executorProvider(Promise, $timeout, timefilter) {
.then(service.handleResponse || noop)
.catch(service.handleError || noop);
}))
.finally(reset);
.finally(reset);
}
function reFetch() {

View file

@ -11,7 +11,7 @@ import 'ui/vis/map/service_settings';
const module = uiModules.get('kibana/region_map', ['kibana']);
module.controller('KbnRegionMapController', function ($scope, $element, Private, Notifier, getAppState,
serviceSettings, config) {
serviceSettings, config) {
const DEFAULT_ZOOM_SETTINGS = {
zoom: 2,

View file

@ -28,30 +28,30 @@ const linkReqRespStats = function ($scope) {
};
SpyModesRegistryProvider
.register(function () {
return {
name: 'request',
display: 'Request',
order: 2,
template: reqRespStatsHTML,
link: linkReqRespStats
};
})
.register(function () {
return {
name: 'response',
display: 'Response',
order: 3,
template: reqRespStatsHTML,
link: linkReqRespStats
};
})
.register(function () {
return {
name: 'stats',
display: 'Statistics',
order: 4,
template: reqRespStatsHTML,
link: linkReqRespStats
};
});
.register(function () {
return {
name: 'request',
display: 'Request',
order: 2,
template: reqRespStatsHTML,
link: linkReqRespStats
};
})
.register(function () {
return {
name: 'response',
display: 'Response',
order: 3,
template: reqRespStatsHTML,
link: linkReqRespStats
};
})
.register(function () {
return {
name: 'stats',
display: 'Statistics',
order: 4,
template: reqRespStatsHTML,
link: linkReqRespStats
};
});

View file

@ -5,15 +5,15 @@ import uiRoutes from 'ui/routes';
uiRoutes.enable();
uiRoutes
.when('/', {
resolve: {
url: function (AppState, globalState, $window) {
const redirectUrl = chrome.getInjected('redirectUrl');
.when('/', {
resolve: {
url: function (AppState, globalState, $window) {
const redirectUrl = chrome.getInjected('redirectUrl');
const hashedUrl = hashUrl([new AppState(), globalState], redirectUrl);
const url = chrome.addBasePath(hashedUrl);
const hashedUrl = hashUrl([new AppState(), globalState], redirectUrl);
const url = chrome.addBasePath(hashedUrl);
$window.location = url;
$window.location = url;
}
}
}
});
});

View file

@ -7,85 +7,85 @@ import { uiModules } from 'ui/modules';
const chrome = require('ui/chrome')
.setRootTemplate(require('plugins/status_page/status_page.html'))
.setRootController('ui', function ($http, buildNum, buildSha) {
const ui = this;
ui.loading = false;
.setRootTemplate(require('plugins/status_page/status_page.html'))
.setRootController('ui', function ($http, buildNum, buildSha) {
const ui = this;
ui.loading = false;
ui.buildInfo = {
num: buildNum,
sha: buildSha.substr(0, 8)
};
ui.buildInfo = {
num: buildNum,
sha: buildSha.substr(0, 8)
};
ui.refresh = function () {
ui.loading = true;
ui.refresh = function () {
ui.loading = true;
// go ahead and get the info you want
return $http
.get(chrome.addBasePath('/api/status'))
.then(function (resp) {
// go ahead and get the info you want
return $http
.get(chrome.addBasePath('/api/status'))
.then(function (resp) {
if (ui.fetchError) {
ui.fetchError.clear();
ui.fetchError = null;
}
if (ui.fetchError) {
ui.fetchError.clear();
ui.fetchError = null;
}
const data = resp.data;
const metrics = data.metrics;
if (metrics) {
ui.metrics = [{
name: 'Heap Total',
value: _.get(metrics, 'process.mem.heap_max_in_bytes'),
type: 'byte'
}, {
name: 'Heap Used',
value: _.get(metrics, 'process.mem.heap_used_in_bytes'),
type: 'byte'
}, {
name: 'Load',
value: [
_.get(metrics, 'os.cpu.load_average.1m'),
_.get(metrics, 'os.cpu.load_average.5m'),
_.get(metrics, 'os.cpu.load_average.15m')
],
type: 'float'
}, {
name: 'Response Time Avg',
value: _.get(metrics, 'response_times.avg_in_millis'),
type: 'ms'
}, {
name: 'Response Time Max',
value: _.get(metrics, 'response_times.max_in_millis'),
type: 'ms'
}, {
name: 'Requests Per Second',
value: _.get(metrics, 'requests.total') * 1000 / _.get(metrics, 'collection_interval_in_millis')
}];
}
const data = resp.data;
const metrics = data.metrics;
if (metrics) {
ui.metrics = [{
name: 'Heap Total',
value: _.get(metrics, 'process.mem.heap_max_in_bytes'),
type: 'byte'
}, {
name: 'Heap Used',
value: _.get(metrics, 'process.mem.heap_used_in_bytes'),
type: 'byte'
}, {
name: 'Load',
value: [
_.get(metrics, 'os.cpu.load_average.1m'),
_.get(metrics, 'os.cpu.load_average.5m'),
_.get(metrics, 'os.cpu.load_average.15m')
],
type: 'float'
}, {
name: 'Response Time Avg',
value: _.get(metrics, 'response_times.avg_in_millis'),
type: 'ms'
}, {
name: 'Response Time Max',
value: _.get(metrics, 'response_times.max_in_millis'),
type: 'ms'
}, {
name: 'Requests Per Second',
value: _.get(metrics, 'requests.total') * 1000 / _.get(metrics, 'collection_interval_in_millis')
}];
}
ui.name = data.name;
ui.statuses = data.status.statuses;
ui.name = data.name;
ui.statuses = data.status.statuses;
const overall = data.status.overall;
if (!ui.serverState || (ui.serverState !== overall.state)) {
ui.serverState = overall.state;
ui.serverStateMessage = overall.title;
}
})
.catch(function () {
if (ui.fetchError) return;
ui.fetchError = notify.error('Failed to request server ui. Perhaps your server is down?');
ui.metrics = ui.statuses = ui.overall = null;
})
.then(function () {
ui.loading = false;
});
};
const overall = data.status.overall;
if (!ui.serverState || (ui.serverState !== overall.state)) {
ui.serverState = overall.state;
ui.serverStateMessage = overall.title;
}
})
.catch(function () {
if (ui.fetchError) return;
ui.fetchError = notify.error('Failed to request server ui. Perhaps your server is down?');
ui.metrics = ui.statuses = ui.overall = null;
})
.then(function () {
ui.loading = false;
});
};
ui.refresh();
});
ui.refresh();
});
uiModules.get('kibana')
.config(function (appSwitcherEnsureNavigationProvider) {
appSwitcherEnsureNavigationProvider.forceNavigation(true);
});
.config(function (appSwitcherEnsureNavigationProvider) {
appSwitcherEnsureNavigationProvider.forceNavigation(true);
});

View file

@ -3,22 +3,22 @@ import { uiModules } from 'ui/modules';
import statusPageMetricTemplate from 'plugins/status_page/status_page_metric.html';
uiModules
.get('kibana', [])
.filter('statusMetric', function () {
return function (input, type) {
const metrics = [].concat(input);
return metrics.map(function (metric) {
return formatNumber(metric, type);
}).join(', ');
};
})
.directive('statusPageMetric', function () {
return {
restrict: 'E',
template: statusPageMetricTemplate,
scope: {
metric: '=',
},
controllerAs: 'metric'
};
});
.get('kibana', [])
.filter('statusMetric', function () {
return function (input, type) {
const metrics = [].concat(input);
return metrics.map(function (metric) {
return formatNumber(metric, type);
}).join(', ');
};
})
.directive('statusPageMetric', function () {
return {
restrict: 'E',
template: statusPageMetricTemplate,
scope: {
metric: '=',
},
controllerAs: 'metric'
};
});

View file

@ -2,26 +2,26 @@ import { uiModules } from 'ui/modules';
import tableVisParamsTemplate from 'plugins/table_vis/table_vis_params.html';
uiModules.get('kibana/table_vis')
.directive('tableVisParams', function () {
return {
restrict: 'E',
template: tableVisParamsTemplate,
link: function ($scope) {
$scope.totalAggregations = ['sum', 'avg', 'min', 'max', 'count'];
.directive('tableVisParams', function () {
return {
restrict: 'E',
template: tableVisParamsTemplate,
link: function ($scope) {
$scope.totalAggregations = ['sum', 'avg', 'min', 'max', 'count'];
$scope.$watchMulti([
'vis.params.showPartialRows',
'vis.params.showMeticsAtAllLevels'
], function () {
if (!$scope.vis) return;
$scope.$watchMulti([
'vis.params.showPartialRows',
'vis.params.showMeticsAtAllLevels'
], function () {
if (!$scope.vis) return;
const params = $scope.vis.params;
if (params.showPartialRows || params.showMeticsAtAllLevels) {
$scope.metricsAtAllLevels = true;
} else {
$scope.metricsAtAllLevels = false;
}
});
}
};
});
const params = $scope.vis.params;
if (params.showPartialRows || params.showMeticsAtAllLevels) {
$scope.metricsAtAllLevels = true;
} else {
$scope.metricsAtAllLevels = false;
}
});
}
};
});

View file

@ -158,7 +158,7 @@ class TagCloud extends EventEmitter {
async _pickPendingJob() {
return await new Promise((resolve) => {
this._setTimeoutId = setTimeout(async() => {
this._setTimeoutId = setTimeout(async () => {
const job = this._pendingJob;
this._pendingJob = null;
this._setTimeoutId = null;

View file

@ -25,10 +25,10 @@ const findSourceFiles = async (patterns, cwd = fromRoot('.')) => {
});
return chain(matches)
.flatten()
.uniq()
.map(match => resolve(cwd, match))
.value();
.flatten()
.uniq()
.map(match => resolve(cwd, match))
.value();
};
findSourceFiles.symlinks = {};

View file

@ -42,7 +42,7 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState
async render(esResponse, status) {
return new Promise(async(resolve) => {
return new Promise(async (resolve) => {
await this._kibanaMapReady;
if (status.resize) {

View file

@ -39,28 +39,28 @@ require('ui/routes')
resolve: {
savedSheet: function (courier, savedSheets, $route) {
return savedSheets.get($route.current.params.id)
.catch(courier.redirectWhenMissing({
'search': '/'
}));
.catch(courier.redirectWhenMissing({
'search': '/'
}));
}
}
});
app.controller('timelion', function (
$http,
$route,
$routeParams,
$scope,
$timeout,
AppState,
config,
confirmModal,
courier,
kbnUrl,
Notifier,
Private,
timefilter
) {
$http,
$route,
$routeParams,
$scope,
$timeout,
AppState,
config,
confirmModal,
courier,
kbnUrl,
Notifier,
Private,
timefilter
) {
// Keeping this at app scope allows us to keep the current page when the user
// switches to say, the timepicker.
@ -222,30 +222,30 @@ app.controller('timelion', function (
timezone: timezone
}),
})
.then(resp => resp.data)
.catch(resp => { throw resp.data; });
.then(resp => resp.data)
.catch(resp => { throw resp.data; });
httpResult
.then(function (resp) {
dismissNotifications();
$scope.stats = resp.stats;
$scope.sheet = resp.sheet;
_.each(resp.sheet, function (cell) {
if (cell.exception) {
$scope.state.selected = cell.plot;
}
.then(function (resp) {
dismissNotifications();
$scope.stats = resp.stats;
$scope.sheet = resp.sheet;
_.each(resp.sheet, function (cell) {
if (cell.exception) {
$scope.state.selected = cell.plot;
}
});
$scope.running = false;
})
.catch(function (resp) {
$scope.sheet = [];
$scope.running = false;
const err = new Error(resp.message);
err.stack = resp.stack;
notify.error(err);
});
$scope.running = false;
})
.catch(function (resp) {
$scope.sheet = [];
$scope.running = false;
const err = new Error(resp.message);
err.stack = resp.stack;
notify.error(err);
});
};
$scope.safeSearch = _.debounce($scope.search, 500);

View file

@ -1,45 +1,45 @@
import panelRegistryProvider from 'plugins/timelion/lib/panel_registry';
require('ui/modules')
.get('apps/timelion', [])
.directive('chart', function (Private) {
return {
restrict: 'A',
scope: {
seriesList: '=chart', // The flot object, data, config and all
search: '=', // The function to execute to kick off a search
interval: '=' // Required for formatting x-axis ticks
},
link: function ($scope, $elem) {
.get('apps/timelion', [])
.directive('chart', function (Private) {
return {
restrict: 'A',
scope: {
seriesList: '=chart', // The flot object, data, config and all
search: '=', // The function to execute to kick off a search
interval: '=' // Required for formatting x-axis ticks
},
link: function ($scope, $elem) {
const panelRegistry = Private(panelRegistryProvider);
let panelScope = $scope.$new(true);
const panelRegistry = Private(panelRegistryProvider);
let panelScope = $scope.$new(true);
function render(seriesList) {
panelScope.$destroy();
function render(seriesList) {
panelScope.$destroy();
if (!seriesList) return;
if (!seriesList) return;
seriesList.render = seriesList.render || {
type: 'timechart'
};
seriesList.render = seriesList.render || {
type: 'timechart'
};
const panelSchema = panelRegistry.byName[seriesList.render.type];
const panelSchema = panelRegistry.byName[seriesList.render.type];
if (!panelSchema) {
$elem.text('No such panel type: ' + seriesList.render.type);
return;
if (!panelSchema) {
$elem.text('No such panel type: ' + seriesList.render.type);
return;
}
panelScope = $scope.$new(true);
panelScope.seriesList = seriesList;
panelScope.interval = $scope.interval;
panelScope.search = $scope.search;
panelSchema.render(panelScope, $elem);
}
panelScope = $scope.$new(true);
panelScope.seriesList = seriesList;
panelScope.interval = $scope.interval;
panelScope.search = $scope.search;
panelSchema.render(panelScope, $elem);
$scope.$watch('seriesList', render);
}
$scope.$watch('seriesList', render);
}
};
});
};
});

View file

@ -17,14 +17,14 @@ export default function (directory) {
// Get a list of all directories with an index.js, use the directory name as the key in the object
const directories = _.chain(glob.sync(path.resolve(__dirname, '../' + directory + '/*/index.js')))
.filter(function (file) {
return file.match(/__test__/) == null;
})
.map(function (file) {
const parts = file.split('/');
const name = parts[parts.length - 2];
return getTuple(directory, name);
}).value();
.filter(function (file) {
return file.match(/__test__/) == null;
})
.map(function (file) {
const parts = file.split('/');
const name = parts[parts.length - 2];
return getTuple(directory, name);
}).value();
const functions = _.zipObject(files.concat(directories));

View file

@ -49,18 +49,18 @@ describe(filename, () => {
_shards: { total: 0 }
});
return invoke(es, [5], tlConfig)
.then(expect.fail)
.catch((e) => {
expect(e).to.be.an('error');
});
.then(expect.fail)
.catch((e) => {
expect(e).to.be.an('error');
});
});
it('returns a seriesList', () => {
tlConfig = stubRequestAndServer(esResponse);
return invoke(es, [5], tlConfig)
.then((r) => {
expect(r.output.type).to.eql('seriesList');
});
.then((r) => {
expect(r.output.type).to.eql('seriesList');
});
});
});

View file

@ -55,10 +55,10 @@ describe(filename, () => {
it('does not allow undefined symbols', () => {
return invoke(fn, [seriesList, null, null, null, null, 'beer'])
.then(expect.fail)
.catch((e) => {
expect(e).to.be.an('error');
});
.then(expect.fail)
.catch((e) => {
expect(e).to.be.an('error');
});
});
});

View file

@ -67,10 +67,10 @@ describe(filename, function () {
it('should throw an error is passed an unsupported interval', function () {
return invoke(fn, [], { time: { interval: '2d' } })
.then(expect.fail)
.catch(function (r) {
expect(r).to.be.an('error');
});
.then(expect.fail)
.catch(function (r) {
expect(r).to.be.an('error');
});
});
it('should use the configured API key when talking to quandl', function () {

View file

@ -16,7 +16,7 @@ function initSeasonalComponents(samplePoints, seasonLength) {
let currentSeason = [];
const seasonalAverages = _.reduce(samplePoints, (result, point, i) => {
currentSeason.push(point);
// If this is the end of the season, add it to the result;
// If this is the end of the season, add it to the result;
if (i % seasonLength === seasonLength - 1) {
result.push(_.sum(currentSeason) / seasonLength);
currentSeason = [];

View file

@ -58,9 +58,9 @@ export default new Chainable('movingaverage', {
function toPoint(point, pairSlice) {
const average = _.chain(pairSlice)
.map(1).reduce(function (memo, num) {
return (memo + num);
}).value() / _window;
.map(1).reduce(function (memo, num) {
return (memo + num);
}).value() / _window;
return [point[0], average];
}

View file

@ -26,18 +26,18 @@ export default new Chainable('movingstd', {
if (i < _window) { return [point[0], null]; }
const average = _.chain(pairs.slice(i - _window, i))
.map(function (point) {
return point[1];
}).reduce(function (memo, num) {
return (memo + num);
}).value() / _window;
.map(function (point) {
return point[1];
}).reduce(function (memo, num) {
return (memo + num);
}).value() / _window;
const variance = _.chain(pairs.slice(i - _window, i))
.map(function (point) {
return point[1];
}).reduce(function (memo, num) {
return memo + Math.pow(num - average, 2);
}).value() / (_window - 1);
.map(function (point) {
return point[1];
}).reduce(function (memo, num) {
return memo + Math.pow(num - average, 2);
}).value() / (_window - 1);
return [point[0], Math.sqrt(variance)];
});

View file

@ -27,20 +27,20 @@ export default function GeoHashGridAggResponseFixture() {
const buckets = _.times(_.random(40, 200), function () {
return _.sample(geoHashCharts, 3).join('');
})
.sort()
.map(function (geoHash) {
const count = _.random(1, 5000);
.sort()
.map(function (geoHash) {
const count = _.random(1, 5000);
docCount += count;
docCount += count;
return {
key: geoHash,
doc_count: count,
1: {
value: 2048 + i
}
};
});
return {
key: geoHash,
doc_count: count,
1: {
value: 2048 + i
}
};
});
return {
key: 'tag ' + (i + 1),

View file

@ -87,32 +87,32 @@ export default () => Joi.object({
silent: Joi.boolean().default(false),
quiet: Joi.boolean()
.when('silent', {
is: true,
then: Joi.default(true).valid(true),
otherwise: Joi.default(false)
}),
.when('silent', {
is: true,
then: Joi.default(true).valid(true),
otherwise: Joi.default(false)
}),
verbose: Joi.boolean()
.when('quiet', {
is: true,
then: Joi.valid(false).default(false),
otherwise: Joi.default(false)
}),
.when('quiet', {
is: true,
then: Joi.valid(false).default(false),
otherwise: Joi.default(false)
}),
events: Joi.any().default({}),
dest: Joi.string().default('stdout'),
filter: Joi.any().default({}),
json: Joi.boolean()
.when('dest', {
is: 'stdout',
then: Joi.default(!process.stdout.isTTY),
otherwise: Joi.default(true)
}),
.when('dest', {
is: 'stdout',
then: Joi.default(!process.stdout.isTTY),
otherwise: Joi.default(true)
}),
useUTC: Joi.boolean().default(true),
})
.default(),
.default(),
ops: Joi.object({
interval: Joi.number().default(5000),

View file

@ -119,7 +119,7 @@ export default async function (kbnServer, server, config) {
search: req.url.search,
pathname: pathPrefix + path.slice(0, -1),
}))
.permanent(true);
.permanent(true);
}
});
@ -169,4 +169,4 @@ export default async function (kbnServer, server, config) {
kbnServer.mixin(versionCheckMixin);
return kbnServer.mixin(xsrfMixin);
};
}

View file

@ -28,7 +28,7 @@ export const createFieldsForTimePatternRoute = pre => ({
lookBack,
metaFields
})
.then(fields => ({ fields }))
.then(fields => ({ fields }))
);
}
}

View file

@ -23,7 +23,7 @@ export const createFieldsForWildcardRoute = pre => ({
pattern,
metaFields
})
.then(fields => ({ fields }))
.then(fields => ({ fields }))
);
}
}

View file

@ -49,14 +49,14 @@ export default class KbnLoggerStringFormat extends LogFormat {
const msg = data.error ? color('error')(data.error.stack) : color('message')(data.message);
const tags = _(data.tags)
.sortBy(function (tag) {
if (color(tag) === _.identity) return `2${tag}`;
if (_.includes(statuses, tag)) return `0${tag}`;
return `1${tag}`;
})
.reduce(function (s, t) {
return s + `[${ color(t)(t) }]`;
}, '');
.sortBy(function (tag) {
if (color(tag) === _.identity) return `2${tag}`;
if (_.includes(statuses, tag)) return `0${tag}`;
return `1${tag}`;
})
.reduce(function (s, t) {
return s + `[${ color(t)(t) }]`;
}, '');
return `${workerType}${type(data.type)} [${time}] ${tags} ${msg}`;
}

View file

@ -11,45 +11,45 @@ export default Promise.method(function (kbnServer, server, config) {
const pid = String(process.pid);
return writeFile(path, pid, { flag: 'wx' })
.catch(function (err) {
if (err.code !== 'EEXIST') throw err;
.catch(function (err) {
if (err.code !== 'EEXIST') throw err;
const log = {
tmpl: 'pid file already exists at <%= path %>',
path: path,
pid: pid
};
const log = {
tmpl: 'pid file already exists at <%= path %>',
path: path,
pid: pid
};
if (config.get('pid.exclusive')) {
throw Boom.create(500, _.template(log.tmpl)(log), log);
} else {
server.log(['pid', 'warning'], log);
}
if (config.get('pid.exclusive')) {
throw Boom.create(500, _.template(log.tmpl)(log), log);
} else {
server.log(['pid', 'warning'], log);
}
return writeFile(path, pid);
})
.then(function () {
return writeFile(path, pid);
})
.then(function () {
server.log(['pid', 'debug'], {
tmpl: 'wrote pid file to <%= path %>',
path: path,
pid: pid
server.log(['pid', 'debug'], {
tmpl: 'wrote pid file to <%= path %>',
path: path,
pid: pid
});
const clean = _.once(function () {
unlink(path);
});
process.once('exit', clean); // for "natural" exits
process.once('SIGINT', function () { // for Ctrl-C exits
clean();
// resend SIGINT
process.kill(process.pid, 'SIGINT');
});
process.on('unhandledRejection', function (reason) {
server.log(['warning'], `Detected an unhandled Promise rejection.\n${reason}`);
});
});
const clean = _.once(function () {
unlink(path);
});
process.once('exit', clean); // for "natural" exits
process.once('SIGINT', function () { // for Ctrl-C exits
clean();
// resend SIGINT
process.kill(process.pid, 'SIGINT');
});
process.on('unhandledRejection', function (reason) {
server.log(['warning'], `Detected an unhandled Promise rejection.\n${reason}`);
});
});
});

View file

@ -22,4 +22,4 @@ export default async function (kbnServer, server, config) {
}
return;
};
}

View file

@ -33,4 +33,4 @@ export default async function (kbnServer, server) {
}
return;
};
}

Some files were not shown because too many files have changed in this diff Show more