mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Merge branch 'master' into feature/ingest
This commit is contained in:
commit
02f24b176b
101 changed files with 1138 additions and 761 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
|||
.aws-config.json
|
||||
.signing-config.json
|
||||
.ackrc
|
||||
.DS_Store
|
||||
.node_binaries
|
||||
|
|
|
@ -1 +1 @@
|
|||
4.3.2
|
||||
4.4.4
|
||||
|
|
|
@ -89,4 +89,5 @@ module.exports = function (grunt) {
|
|||
// load task definitions
|
||||
grunt.task.loadTasks('tasks');
|
||||
grunt.task.loadTasks('tasks/build');
|
||||
grunt.task.loadTasks('tasks/rebuild');
|
||||
};
|
||||
|
|
12
README.md
12
README.md
|
@ -41,9 +41,9 @@ Visit [Elastic.co](http://www.elastic.co/guide/en/kibana/current/index.html) for
|
|||
|
||||
For the daring, snapshot builds are available. These builds are created after each commit to the master branch, and therefore are not something you should run in production.
|
||||
|
||||
| platform | | |
|
||||
| --- | --- | --- |
|
||||
| OSX | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-darwin-x64.tar.gz) | [zip](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-darwin-x64.zip) |
|
||||
| Linux x64 | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-linux-x64.tar.gz) | [zip](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-linux-x64.zip) |
|
||||
| Linux x86 | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-linux-x86.tar.gz) | [zip](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-linux-x86.zip) |
|
||||
| Windows | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-windows.tar.gz) | [zip](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-windows.zip) |
|
||||
| platform | | | | |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| OSX | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-darwin-x64.tar.gz) | [zip](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-darwin-x64.zip) | | |
|
||||
| Linux x64 | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-linux-x64.tar.gz) | [zip](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-linux-x64.zip) | [deb](https://download.elastic.co/kibana/kibana-snapshot/kibana_5.0.0-snapshot_amd64.deb)| [rpm](https://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0_snapshot-1.x86_64.rpm) |
|
||||
| Linux x86 | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-linux-x86.tar.gz) | [zip](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-linux-x86.zip) | [deb](https://download.elastic.co/kibana/kibana-snapshot/kibana_5.0.0-snapshot_i386.deb) | [rpm](https://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0_snapshot-1.i386.rpm) |
|
||||
| Windows | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-windows.tar.gz) | [zip](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-windows.zip) | | |
|
||||
|
|
|
@ -6,7 +6,7 @@ key Kibana functionality. By the end of this tutorial, you will have:
|
|||
|
||||
* Loaded a sample data set into your Elasticsearch installation
|
||||
* Defined at least one index pattern
|
||||
* Use the <<discover, Discover>> functionality to explore your data
|
||||
* Used the <<discover, Discover>> functionality to explore your data
|
||||
* Set up some <<visualize,_visualizations_>> to graphically represent your data
|
||||
* Assembled visualizations into a <<dashboard,Dashboard>>
|
||||
|
||||
|
|
|
@ -44,3 +44,5 @@ error messages.
|
|||
information and all requests.
|
||||
`ops.interval`:: *Default: 10000* Set the interval in milliseconds to sample system and process performance metrics.
|
||||
The minimum value is 100.
|
||||
`status.allowAnonymous`:: *Default: false* If authentication is enabled, setting this to `true` allows
|
||||
unauthenticated users to access the Kibana server status API and status page.
|
||||
|
|
|
@ -197,7 +197,7 @@
|
|||
"supertest-as-promised": "2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.3.2",
|
||||
"npm": "2.14.22"
|
||||
"node": "4.4.4",
|
||||
"npm": "2.15.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,9 @@ module.exports = function (kibana) {
|
|||
main: 'plugins/console/console',
|
||||
icon: 'plugins/console/logo.svg',
|
||||
injectVars: function (server, options) {
|
||||
return options;
|
||||
const varsToInject = options;
|
||||
varsToInject.elasticsearchUrl = server.config().get('elasticsearch.url');
|
||||
return varsToInject;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
@ -33,11 +35,11 @@ module.exports = function (kibana) {
|
|||
|
||||
return new kibana.Plugin({
|
||||
id: 'console',
|
||||
require: [ 'elasticsearch' ],
|
||||
|
||||
config: function (Joi) {
|
||||
return Joi.object({
|
||||
enabled: Joi.boolean().default(true),
|
||||
defaultServerUrl: Joi.string().default('http://localhost:9200'),
|
||||
proxyFilter: Joi.array().items(Joi.string()).single().default(['.*']),
|
||||
ssl: Joi.object({
|
||||
verify: Joi.boolean(),
|
||||
|
@ -88,10 +90,7 @@ module.exports = function (kibana) {
|
|||
const proxyRouteConfig = {
|
||||
validate: {
|
||||
query: Joi.object().keys({
|
||||
uri: Joi.string().uri({
|
||||
allowRelative: false,
|
||||
shema: ['http:', 'https:'],
|
||||
}),
|
||||
uri: Joi.string()
|
||||
}).unknown(true),
|
||||
},
|
||||
|
||||
|
@ -111,15 +110,23 @@ module.exports = function (kibana) {
|
|||
],
|
||||
|
||||
handler(req, reply) {
|
||||
const { uri } = req.query;
|
||||
let baseUri = server.config().get('elasticsearch.url');
|
||||
let { uri:path } = req.query;
|
||||
|
||||
baseUri = baseUri.replace(/\/+$/, '');
|
||||
path = path.replace(/^\/+/, '');
|
||||
const uri = baseUri + '/' + path;
|
||||
|
||||
const requestHeadersWhitelist = server.config().get('elasticsearch.requestHeadersWhitelist');
|
||||
const filterHeaders = server.plugins.elasticsearch.filterHeaders;
|
||||
reply.proxy({
|
||||
uri,
|
||||
mapUri: function (request, done) {
|
||||
done(null, uri, filterHeaders(request.headers, requestHeadersWhitelist))
|
||||
},
|
||||
xforward: true,
|
||||
passThrough: true,
|
||||
onResponse(err, res, request, reply, settings, ttl) {
|
||||
if (err != null) {
|
||||
reply("Error connecting to '" + request.query.uri + "':\n\n" + err.message).type("text/plain").statusCode = 502;
|
||||
reply("Error connecting to '" + uri + "':\n\n" + err.message).type("text/plain").statusCode = 502;
|
||||
} else {
|
||||
reply(null, res);
|
||||
}
|
||||
|
|
|
@ -10,15 +10,10 @@ let utils = require('./utils');
|
|||
let _ = require('lodash');
|
||||
const chrome = require('ui/chrome');
|
||||
|
||||
const defaultServerUrl = chrome.getInjected('defaultServerUrl');
|
||||
|
||||
$(document.body).removeClass('fouc');
|
||||
|
||||
// set the value of the server and/or the input and clear the output
|
||||
function resetToValues(server, content) {
|
||||
if (server != null) {
|
||||
es.setBaseUrl(server);
|
||||
}
|
||||
// set the value of the input and clear the output
|
||||
function resetToValues(content) {
|
||||
if (content != null) {
|
||||
input.update(content);
|
||||
}
|
||||
|
@ -31,10 +26,10 @@ function loadSavedState() {
|
|||
|
||||
if (sourceLocation == "stored") {
|
||||
if (previousSaveState) {
|
||||
resetToValues(previousSaveState.server, previousSaveState.content);
|
||||
resetToValues(previousSaveState.content);
|
||||
}
|
||||
else {
|
||||
resetToValues(defaultServerUrl);
|
||||
resetToValues();
|
||||
input.autoIndent();
|
||||
}
|
||||
}
|
||||
|
@ -44,17 +39,14 @@ function loadSavedState() {
|
|||
loadFrom.headers = {Accept: "application/vnd.github.v3.raw"};
|
||||
}
|
||||
$.ajax(loadFrom).done(function (data) {
|
||||
resetToValues(defaultServerUrl, data);
|
||||
resetToValues(data);
|
||||
input.moveToNextRequestEdge(true);
|
||||
input.highlightCurrentRequestsAndUpdateActionBar();
|
||||
input.updateActionsBar();
|
||||
});
|
||||
}
|
||||
else if (previousSaveState) {
|
||||
resetToValues(previousSaveState.server);
|
||||
}
|
||||
else {
|
||||
resetToValues(defaultServerUrl);
|
||||
resetToValues();
|
||||
}
|
||||
input.moveToNextRequestEdge(true);
|
||||
}
|
||||
|
@ -69,15 +61,12 @@ function setupAutosave() {
|
|||
}
|
||||
timer = setTimeout(saveCurrentState, saveDelay);
|
||||
});
|
||||
|
||||
es.addServerChangeListener(saveCurrentState);
|
||||
}
|
||||
|
||||
function saveCurrentState() {
|
||||
try {
|
||||
var content = input.getValue();
|
||||
var server = es.getBaseUrl();
|
||||
history.updateCurrentState(server, content);
|
||||
history.updateCurrentState(content);
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Ignoring saving error: " + e);
|
||||
|
|
|
@ -32,13 +32,4 @@ module.controller('SenseController', function SenseController($scope, docTitle)
|
|||
event.preventDefault();
|
||||
input.focus();
|
||||
};
|
||||
|
||||
this.serverUrl = es.getBaseUrl();
|
||||
|
||||
// read server url changes into scope
|
||||
es.addServerChangeListener((server) => {
|
||||
$scope.$evalAsync(() => {
|
||||
this.serverUrl = server;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,27 +7,3 @@
|
|||
</span>
|
||||
</div>
|
||||
</kbn-top-nav>
|
||||
|
||||
<navbar ng-show="chrome.getVisible()" name="sense-serverInput">
|
||||
<form
|
||||
name="serverInput"
|
||||
class="fill inline-form"
|
||||
ng-submit="sense.sendSelected()"
|
||||
role="form">
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="http://servername:port"
|
||||
aria-label="Server Name"
|
||||
class="form-control"
|
||||
ng-focus="navbar.updateServerUrlHistory()"
|
||||
ng-blur="navbar.commitServerUrlFormModel()"
|
||||
|
||||
sense-uib-typeahead="url for url in navbar.serverUrlHistory"
|
||||
ng-model="navbar.serverUrlFormModel"
|
||||
typeahead-append-to-body="true"
|
||||
typeahead-focus-first="false"
|
||||
|
||||
required>
|
||||
</form>
|
||||
</navbar>
|
||||
|
|
|
@ -14,7 +14,6 @@ require('ui/modules')
|
|||
scope: {},
|
||||
link($scope, $el, attrs, sense) {
|
||||
$scope.sense = sense
|
||||
$scope.navbar.link($scope)
|
||||
},
|
||||
controllerAs: 'navbar',
|
||||
controller: class SenseNavbarController {
|
||||
|
@ -49,21 +48,6 @@ require('ui/modules')
|
|||
this.menu.open('welcome')
|
||||
}
|
||||
|
||||
this.updateServerUrlHistory();
|
||||
}
|
||||
|
||||
link($scope) {
|
||||
$scope.$watch('sense.serverUrl', (url) => {
|
||||
this.serverUrlFormModel = url
|
||||
})
|
||||
}
|
||||
|
||||
updateServerUrlHistory() {
|
||||
this.serverUrlHistory = history.getHistoricalServers();
|
||||
}
|
||||
|
||||
commitServerUrlFormModel() {
|
||||
es.setBaseUrl(this.serverUrlFormModel);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
let _ = require('lodash');
|
||||
let $ = require('jquery');
|
||||
|
||||
let baseUrl;
|
||||
let serverChangeListeners = [];
|
||||
let esVersion = [];
|
||||
|
||||
module.exports.getBaseUrl = function () {
|
||||
return baseUrl;
|
||||
};
|
||||
module.exports.getVersion = function () {
|
||||
return esVersion;
|
||||
};
|
||||
|
@ -15,15 +10,7 @@ module.exports.getVersion = function () {
|
|||
module.exports.send = function (method, path, data, server, disable_auth_alert) {
|
||||
var wrappedDfd = $.Deferred();
|
||||
|
||||
server = server || exports.getBaseUrl();
|
||||
path = exports.constructESUrl(server, path);
|
||||
var uname_password_re = /^(https?:\/\/)?(?:(?:([^\/]*):)?([^\/]*?)@)?(.*)$/;
|
||||
var url_parts = path.match(uname_password_re);
|
||||
|
||||
var uname = url_parts[2];
|
||||
var password = url_parts[3];
|
||||
path = url_parts[1] + url_parts[4];
|
||||
console.log("Calling " + path + " (uname: " + uname + " pwd: " + password + ")");
|
||||
console.log("Calling " + path);
|
||||
if (data && method == "GET") {
|
||||
method = "POST";
|
||||
}
|
||||
|
@ -37,8 +24,6 @@ module.exports.send = function (method, path, data, server, disable_auth_alert)
|
|||
cache: false,
|
||||
crossDomain: true,
|
||||
type: method,
|
||||
password: password,
|
||||
username: uname,
|
||||
dataType: "text", // disable automatic guessing
|
||||
};
|
||||
|
||||
|
@ -56,61 +41,8 @@ module.exports.send = function (method, path, data, server, disable_auth_alert)
|
|||
return wrappedDfd;
|
||||
};
|
||||
|
||||
module.exports.constructESUrl = function (server, path) {
|
||||
if (!path) {
|
||||
path = server;
|
||||
server = exports.getBaseUrl();
|
||||
}
|
||||
if (path.indexOf("://") >= 0) {
|
||||
return path;
|
||||
}
|
||||
if (server.indexOf("://") < 0) {
|
||||
server = (document.location.protocol || "http:") + "//" + server;
|
||||
}
|
||||
if (server.substr(-1) == "/") {
|
||||
server = server.substr(0, server.length - 1);
|
||||
}
|
||||
if (path.charAt(0) === "/") {
|
||||
path = path.substr(1);
|
||||
}
|
||||
|
||||
return server + "/" + path;
|
||||
};
|
||||
|
||||
module.exports.forceRefresh = function () {
|
||||
exports.setBaseUrl(baseUrl, true)
|
||||
};
|
||||
|
||||
module.exports.setBaseUrl = function (base, force) {
|
||||
if (baseUrl !== base || force) {
|
||||
var old = baseUrl;
|
||||
baseUrl = base;
|
||||
exports.send("GET", "/").done(function (data, status, xhr) {
|
||||
if (xhr.status === 200) {
|
||||
// parse for version
|
||||
var value = xhr.responseText;
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
if (value.version && value.version.number) {
|
||||
esVersion = value.version.number.split(".");
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
|
||||
}
|
||||
}
|
||||
_.each(serverChangeListeners, function (cb) {
|
||||
cb(base, old)
|
||||
});
|
||||
}).fail(function () {
|
||||
esVersion = []; // unknown
|
||||
_.each(serverChangeListeners, function (cb) {
|
||||
cb(base, old)
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.addServerChangeListener = function (cb) {
|
||||
serverChangeListeners.push(cb);
|
||||
module.exports.constructESUrl = function (baseUri, path) {
|
||||
baseUri = baseUri.replace(/\/+$/, '');
|
||||
path = path.replace(/^\/+/, '');
|
||||
return baseUri + '/' + path;
|
||||
};
|
||||
|
|
|
@ -3,8 +3,6 @@ const { uniq } = require('lodash');
|
|||
const storage = require('./storage');
|
||||
const chrome = require('ui/chrome');
|
||||
|
||||
const defaultServerUrl = chrome.getInjected('defaultServerUrl');
|
||||
|
||||
const history = module.exports = {
|
||||
restoreFromHistory() {
|
||||
// default method for history.restoreFromHistory
|
||||
|
@ -26,11 +24,7 @@ const history = module.exports = {
|
|||
.map(key => storage.get(key));
|
||||
},
|
||||
|
||||
getHistoricalServers() {
|
||||
return uniq(history.getHistory().map(req => req.server));
|
||||
},
|
||||
|
||||
addToHistory(server, endpoint, method, data) {
|
||||
addToHistory(endpoint, method, data) {
|
||||
var keys = history.getHistoryKeys();
|
||||
keys.splice(0, 500); // only maintain most recent X;
|
||||
$.each(keys, function (i, k) {
|
||||
|
@ -41,18 +35,16 @@ const history = module.exports = {
|
|||
var k = "hist_elem_" + timestamp;
|
||||
storage.set(k, {
|
||||
time: timestamp,
|
||||
server: server,
|
||||
endpoint: endpoint,
|
||||
method: method,
|
||||
data: data
|
||||
});
|
||||
},
|
||||
|
||||
updateCurrentState(server, content) {
|
||||
updateCurrentState(content) {
|
||||
var timestamp = new Date().getTime();
|
||||
storage.set("editor_state", {
|
||||
time: timestamp,
|
||||
server: server === defaultServerUrl ? undefined : server,
|
||||
content: content
|
||||
});
|
||||
},
|
||||
|
@ -60,8 +52,8 @@ const history = module.exports = {
|
|||
getSavedEditorState() {
|
||||
const saved = storage.get('editor_state');
|
||||
if (!saved) return;
|
||||
const { time, server = defaultServerUrl, content } = saved;
|
||||
return { time, server, content };
|
||||
const { time, content } = saved;
|
||||
return { time, content };
|
||||
},
|
||||
|
||||
clearHistory($el) {
|
||||
|
|
|
@ -158,7 +158,7 @@ function sendCurrentRequestToES() {
|
|||
((xhr.status >= 200 && xhr.status < 300) || xhr.status == 404)
|
||||
) {
|
||||
// we have someone on the other side. Add to history
|
||||
history.addToHistory(es.getBaseUrl(), es_path, es_method, es_data);
|
||||
history.addToHistory(es_path, es_method, es_data);
|
||||
|
||||
|
||||
let value = xhr.responseText;
|
||||
|
|
|
@ -238,27 +238,7 @@ function setActiveApi(api) {
|
|||
ACTIVE_API = api;
|
||||
}
|
||||
|
||||
es.addServerChangeListener(function () {
|
||||
var version = es.getVersion() || [];
|
||||
var api;
|
||||
|
||||
switch (version[0]) {
|
||||
case '5':
|
||||
api = 'es_5_0';
|
||||
break;
|
||||
case '2':
|
||||
api = 'es_2_0';
|
||||
break;
|
||||
case '1':
|
||||
default:
|
||||
api = 'es_1_0';
|
||||
}
|
||||
|
||||
if (api) {
|
||||
setActiveApi(api);
|
||||
}
|
||||
|
||||
});
|
||||
setActiveApi('es_5_0');
|
||||
|
||||
module.exports.setActiveApi = setActiveApi;
|
||||
module.exports.getGlobalAutocompleteComponents = getGlobalAutocompleteComponents;
|
||||
|
|
|
@ -279,8 +279,6 @@ function autocomplete_retriever() {
|
|||
}, 60000);
|
||||
}
|
||||
|
||||
es.addServerChangeListener(retrieveAutocompleteInfoFromServer);
|
||||
|
||||
module.exports = _.assign(mappingObj, {
|
||||
getFields: getFields,
|
||||
getIndices: getIndices,
|
||||
|
|
|
@ -6,6 +6,8 @@ let RowParser = require('./row_parser');
|
|||
let InputMode = require('./mode/input');
|
||||
let utils = require('../utils');
|
||||
let es = require('../es');
|
||||
import chrome from 'ui/chrome';
|
||||
|
||||
const smartResize = require('../smart_resize');
|
||||
|
||||
function isInt(x) {
|
||||
|
@ -538,7 +540,8 @@ function SenseEditor($el) {
|
|||
es_method = req.method,
|
||||
es_data = req.data;
|
||||
|
||||
var url = es.constructESUrl(es.getBaseUrl() || "localhost:9200", es_path);
|
||||
const elasticsearchBaseUrl = chrome.getInjected('elasticsearchUrl');
|
||||
var url = es.constructESUrl(elasticsearchBaseUrl, es_path);
|
||||
|
||||
var ret = 'curl -X' + es_method + ' "' + url + '"';
|
||||
if (es_data && es_data.length) {
|
||||
|
|
|
@ -61,7 +61,6 @@ function updateSettings({ fontSize, wrapMode, autocomplete}) {
|
|||
setWrapMode(wrapMode);
|
||||
setAutocomplete(autocomplete);
|
||||
require('./input').focus();
|
||||
es.forceRefresh();
|
||||
return getCurrentSettings();
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ var {test, module, ok, fail, asyncTest, deepEqual, equal, start} = QUnit;
|
|||
|
||||
module("Editor", {
|
||||
setup: function () {
|
||||
es.setBaseUrl("http://localhost:9200");
|
||||
input.$el.show();
|
||||
input.autocomplete._test.removeChangeListener();
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ const readFile = (file) => require('fs').readFileSync(file, 'utf8');
|
|||
import util from 'util';
|
||||
import url from 'url';
|
||||
import callWithRequest from './call_with_request';
|
||||
import filterHeaders from './filter_headers';
|
||||
|
||||
module.exports = function (server) {
|
||||
const config = server.config();
|
||||
|
@ -80,6 +81,7 @@ module.exports = function (server) {
|
|||
server.expose('createClient', createClient);
|
||||
server.expose('callWithRequestFactory', _.partial(callWithRequest, server));
|
||||
server.expose('callWithRequest', callWithRequest(server, noAuthClient));
|
||||
server.expose('filterHeaders', filterHeaders);
|
||||
server.expose('errors', elasticsearch.errors);
|
||||
|
||||
return client;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import ingest from './server/routes/api/ingest';
|
||||
import search from './server/routes/api/search';
|
||||
import settings from './server/routes/api/settings';
|
||||
|
||||
module.exports = function (kibana) {
|
||||
return new kibana.Plugin({
|
||||
|
@ -78,6 +79,7 @@ module.exports = function (kibana) {
|
|||
init: function (server, options) {
|
||||
ingest(server);
|
||||
search(server);
|
||||
settings(server);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import angular from 'angular';
|
||||
import ngMock from 'ng_mock';
|
||||
import _ from 'lodash';
|
||||
|
@ -18,19 +17,20 @@ let config;
|
|||
let hits;
|
||||
let indexPattern;
|
||||
let indexPatternList;
|
||||
let shortDotsValue;
|
||||
|
||||
// Sets up the directive, take an element, and a list of properties to attach to the parent scope.
|
||||
const init = function ($elem, props) {
|
||||
ngMock.inject(function ($rootScope, $compile, $timeout, _config_) {
|
||||
shortDotsValue = _config_.get('shortDots:enable');
|
||||
config = _config_;
|
||||
config.set('shortDots:enable', false);
|
||||
$parentScope = $rootScope;
|
||||
_.assign($parentScope, props);
|
||||
$compile($elem)($parentScope);
|
||||
|
||||
// Required for test to run solo. Sigh
|
||||
$timeout(function () {
|
||||
$elem.scope().$digest();
|
||||
}, 0);
|
||||
$timeout(() => $elem.scope().$digest(), 0);
|
||||
|
||||
$scope = $elem.isolateScope();
|
||||
});
|
||||
|
@ -39,6 +39,7 @@ const init = function ($elem, props) {
|
|||
const destroy = function () {
|
||||
$scope.$destroy();
|
||||
$parentScope.$destroy();
|
||||
config.set('shortDots:enable', shortDotsValue);
|
||||
};
|
||||
|
||||
describe('discover field chooser directives', function () {
|
||||
|
@ -80,9 +81,7 @@ describe('discover field chooser directives', function () {
|
|||
$scope.$digest();
|
||||
}));
|
||||
|
||||
afterEach(function () {
|
||||
destroy();
|
||||
});
|
||||
afterEach(() => destroy());
|
||||
|
||||
const getSections = function (ctx) {
|
||||
return {
|
||||
|
@ -108,17 +107,26 @@ describe('discover field chooser directives', function () {
|
|||
|
||||
it('should have 2 popular fields, 1 unpopular field and no selected fields', function (done) {
|
||||
const section = getSections($elem);
|
||||
const popular = find('popular');
|
||||
const unpopular = find('unpopular');
|
||||
|
||||
expect(section.selected.find('li').length).to.be(0);
|
||||
|
||||
expect(section.popular.text()).to.contain('ssl');
|
||||
expect(section.popular.text()).to.contain('@timestamp');
|
||||
expect(section.popular.text()).to.not.contain('ip\n');
|
||||
expect(popular).to.contain('ssl');
|
||||
expect(popular).to.contain('@timestamp');
|
||||
expect(popular).to.not.contain('ip\n');
|
||||
|
||||
expect(section.unpopular.text()).to.contain('extension');
|
||||
expect(section.unpopular.text()).to.contain('machine.os');
|
||||
expect(section.unpopular.text()).to.not.contain('ssl');
|
||||
expect(unpopular).to.contain('extension');
|
||||
expect(unpopular).to.contain('machine.os');
|
||||
expect(unpopular).to.not.contain('ssl');
|
||||
done();
|
||||
|
||||
function find(popularity) {
|
||||
return section[popularity]
|
||||
.find('.discover-field-name')
|
||||
.map((i, el) => $(el).text())
|
||||
.toArray();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -32,16 +32,15 @@ chrome
|
|||
lastUrlStore: window.sessionStorage,
|
||||
activeIndicatorColor: '#656a76'
|
||||
})
|
||||
.setRootController('kibana', function ($scope, $rootScope, courier, config) {
|
||||
function setDefaultTimezone() {
|
||||
moment.tz.setDefault(config.get('dateFormat:tz'));
|
||||
}
|
||||
|
||||
.setRootController('kibana', function ($scope, courier, config) {
|
||||
// wait for the application to finish loading
|
||||
$scope.$on('application.load', function () {
|
||||
courier.start();
|
||||
});
|
||||
|
||||
$scope.$on('init:config', setDefaultTimezone);
|
||||
$scope.$on('change:config.dateFormat:tz', setDefaultTimezone);
|
||||
config.watch('dateFormat:tz', setDefaultTimezone, $scope);
|
||||
|
||||
function setDefaultTimezone(tz) {
|
||||
moment.tz.setDefault(tz);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import _ from 'lodash';
|
||||
import 'ui/elastic_textarea';
|
||||
import ConfigDefaultsProvider from 'ui/config/defaults';
|
||||
import uiModules from 'ui/modules';
|
||||
import advancedRowTemplate from 'plugins/kibana/settings/sections/advanced/advanced_row.html';
|
||||
|
||||
uiModules.get('apps/settings')
|
||||
.directive('advancedRow', function (config, Notifier, Private) {
|
||||
.directive('advancedRow', function (config, Notifier) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
replace: true,
|
||||
|
@ -15,7 +14,6 @@ uiModules.get('apps/settings')
|
|||
configs: '='
|
||||
},
|
||||
link: function ($scope) {
|
||||
const configDefaults = Private(ConfigDefaultsProvider);
|
||||
const notify = new Notifier();
|
||||
const keyCodes = {
|
||||
ESC: 27
|
||||
|
@ -28,10 +26,10 @@ uiModules.get('apps/settings')
|
|||
const loading = function (conf, fn) {
|
||||
conf.loading = true;
|
||||
fn()
|
||||
.finally(function () {
|
||||
conf.loading = conf.editing = false;
|
||||
})
|
||||
.catch(notify.fatal);
|
||||
.then(function () {
|
||||
conf.loading = conf.editing = false;
|
||||
})
|
||||
.catch(notify.fatal);
|
||||
};
|
||||
|
||||
$scope.maybeCancel = function ($event, conf) {
|
||||
|
@ -50,7 +48,7 @@ uiModules.get('apps/settings')
|
|||
$scope.save = function (conf) {
|
||||
loading(conf, function () {
|
||||
if (conf.unsavedValue === conf.defVal) {
|
||||
return config.clear(conf.name);
|
||||
return config.remove(conf.name);
|
||||
}
|
||||
|
||||
return config.set(conf.name, conf.unsavedValue);
|
||||
|
@ -63,7 +61,7 @@ uiModules.get('apps/settings')
|
|||
|
||||
$scope.clear = function (conf) {
|
||||
return loading(conf, function () {
|
||||
return config.clear(conf.name);
|
||||
return config.remove(conf.name);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ import _ from 'lodash';
|
|||
import registry from 'ui/registry/settings_sections';
|
||||
import toEditableConfig from 'plugins/kibana/settings/sections/advanced/lib/to_editable_config';
|
||||
import 'plugins/kibana/settings/sections/advanced/advanced_row';
|
||||
import ConfigDefaultsProvider from 'ui/config/defaults';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import uiModules from 'ui/modules';
|
||||
import indexTemplate from 'plugins/kibana/settings/sections/advanced/index.html';
|
||||
|
@ -17,39 +16,25 @@ uiModules.get('apps/settings')
|
|||
return {
|
||||
restrict: 'E',
|
||||
link: function ($scope) {
|
||||
const configDefaults = Private(ConfigDefaultsProvider);
|
||||
const keyCodes = {
|
||||
ESC: 27
|
||||
};
|
||||
|
||||
function isTypeComplex(conf) {
|
||||
return !(conf.json || conf.array || conf.bool || conf.normal);
|
||||
}
|
||||
|
||||
function notDefaultConfig(configName) {
|
||||
return !(configName in configDefaults);
|
||||
}
|
||||
|
||||
function readConfigVals() {
|
||||
const configVals = config._vals();
|
||||
|
||||
const customConfig = Object.keys(configVals)
|
||||
.filter(notDefaultConfig)
|
||||
.map(name => toEditableConfig(false, name, configVals[name]));
|
||||
|
||||
$scope.configs = _(configDefaults)
|
||||
.map((def, name) => toEditableConfig(def, name, configVals[name]))
|
||||
.reject('readonly')
|
||||
.concat(customConfig)
|
||||
.value();
|
||||
}
|
||||
|
||||
// react to changes of the config values
|
||||
const unhook = $rootScope.$on('change:config', readConfigVals);
|
||||
$scope.$on('$destroy', unhook);
|
||||
config.watchAll(changed, $scope);
|
||||
|
||||
// initial config setup
|
||||
readConfigVals();
|
||||
changed();
|
||||
|
||||
function changed(values) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -82,5 +82,5 @@ describe('Settings', function () {
|
|||
});
|
||||
|
||||
function invoke({ def = false, name = 'woah', value = 'forreal' } = {}) {
|
||||
return toEditableConfig(def, name, value);
|
||||
return toEditableConfig({ def, name, value, isCustom: def === false });
|
||||
}
|
||||
|
|
|
@ -8,10 +8,10 @@ import getEditorType from './get_editor_type';
|
|||
* @param {object} current value of setting
|
||||
* @returns {object} the editable config object
|
||||
*/
|
||||
function toEditableConfig(def, name, value) {
|
||||
const isCustom = !def;
|
||||
if (isCustom) def = {};
|
||||
|
||||
function toEditableConfig({ def, name, value, isCustom }) {
|
||||
if (!def) {
|
||||
def = {};
|
||||
}
|
||||
const conf = {
|
||||
name,
|
||||
value,
|
||||
|
|
|
@ -58,7 +58,7 @@ uiModules.get('apps/settings')
|
|||
|
||||
$scope.removePattern = function () {
|
||||
if ($scope.indexPattern.id === config.get('defaultIndex')) {
|
||||
config.delete('defaultIndex');
|
||||
config.remove('defaultIndex');
|
||||
if (otherIds.length) {
|
||||
config.set('defaultIndex', otherIds[0]);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ uiModules
|
|||
$scope.delete = attrs.delete ? $scope.delete : null;
|
||||
$scope.setDefault = attrs.setDefault ? $scope.setDefault : null;
|
||||
$scope.refreshFields = attrs.refreshFields ? $scope.refreshFields : null;
|
||||
config.$bind($scope, 'defaultIndex');
|
||||
config.bindToScope($scope, 'defaultIndex');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -14,7 +14,7 @@ uiModules.get('apps/settings')
|
|||
|
||||
$scope.showAddNew = !/^\/settings\/indices$/.test($route.current.$$route.originalPath);
|
||||
$scope.editingId = $route.current.params.indexPatternId;
|
||||
config.$bind($scope, 'defaultIndex');
|
||||
config.bindToScope($scope, 'defaultIndex');
|
||||
|
||||
function refreshIndexPatternList() {
|
||||
indexPatterns.getIds.clearCache();
|
||||
|
|
|
@ -241,6 +241,8 @@ uiModules
|
|||
|
||||
$scope.doSave = function () {
|
||||
savedVis.id = savedVis.title;
|
||||
// vis.title was not bound and it's needed to reflect title into visState
|
||||
$state.vis.title = savedVis.title;
|
||||
savedVis.visState = $state.vis;
|
||||
savedVis.uiStateJSON = angular.toJson($scope.uiState.getChanges());
|
||||
|
||||
|
|
6
src/plugins/kibana/server/routes/api/settings/index.js
Normal file
6
src/plugins/kibana/server/routes/api/settings/index.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
export default function (server) {
|
||||
require('./register_get')(server);
|
||||
require('./register_set')(server);
|
||||
require('./register_set_many')(server);
|
||||
require('./register_delete')(server);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import Boom from 'boom';
|
||||
|
||||
export default function registerDelete(server) {
|
||||
server.route({
|
||||
path: '/api/kibana/settings/{key}',
|
||||
method: 'DELETE',
|
||||
handler: function (req, reply) {
|
||||
const { key } = req.params;
|
||||
const uiSettings = server.uiSettings();
|
||||
uiSettings
|
||||
.remove(key)
|
||||
.then(() => uiSettings
|
||||
.getUserProvided()
|
||||
.then(settings => reply({ settings }).type('application/json'))
|
||||
)
|
||||
.catch(reason => reply(Boom.wrap(reason)));
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import Boom from 'boom';
|
||||
|
||||
export default function registerGet(server) {
|
||||
server.route({
|
||||
path: '/api/kibana/settings',
|
||||
method: 'GET',
|
||||
handler: function (req, reply) {
|
||||
server
|
||||
.uiSettings()
|
||||
.getUserProvided()
|
||||
.then(settings => reply({ settings }).type('application/json'))
|
||||
.catch(reason => reply(Boom.wrap(reason)));
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import Boom from 'boom';
|
||||
|
||||
export default function registerSet(server) {
|
||||
server.route({
|
||||
path: '/api/kibana/settings/{key}',
|
||||
method: 'POST',
|
||||
handler: function (req, reply) {
|
||||
const { key } = req.params;
|
||||
const { value } = req.payload;
|
||||
const uiSettings = server.uiSettings();
|
||||
uiSettings
|
||||
.set(key, value)
|
||||
.then(() => uiSettings
|
||||
.getUserProvided()
|
||||
.then(settings => reply({ settings }).type('application/json'))
|
||||
)
|
||||
.catch(reason => reply(Boom.wrap(reason)));
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import Boom from 'boom';
|
||||
|
||||
export default function registerSet(server) {
|
||||
server.route({
|
||||
path: '/api/kibana/settings',
|
||||
method: 'POST',
|
||||
handler: function (req, reply) {
|
||||
const { key } = req.params;
|
||||
const { changes } = req.payload;
|
||||
const uiSettings = server.uiSettings();
|
||||
uiSettings
|
||||
.setMany(changes)
|
||||
.then(() => uiSettings
|
||||
.getUserProvided()
|
||||
.then(settings => reply({ settings }).type('application/json'))
|
||||
)
|
||||
.catch(reason => reply(Boom.wrap(reason)));
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { union } from 'lodash';
|
||||
|
||||
import defaultsProvider from '../../ui/settings/defaults';
|
||||
import findSourceFiles from './find_source_files';
|
||||
import { fromRoot } from '../../utils';
|
||||
|
||||
|
@ -57,6 +57,8 @@ export default (kibana) => {
|
|||
});
|
||||
}
|
||||
|
||||
env.defaultUiSettings = defaultsProvider();
|
||||
|
||||
return new UiBundle({
|
||||
id: 'tests',
|
||||
modules: modules,
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
module.exports = function ({env, bundle}) {
|
||||
|
||||
let pluginSlug = env.pluginInfo.sort()
|
||||
.map(p => ' * - ' + p)
|
||||
.join('\n');
|
||||
const pluginSlug = env.pluginInfo.sort()
|
||||
.map(p => ' * - ' + p)
|
||||
.join('\n');
|
||||
|
||||
let requires = bundle.modules
|
||||
.map(m => `require(${JSON.stringify(m)});`)
|
||||
.join('\n');
|
||||
const requires = bundle.modules
|
||||
.map(m => `require(${JSON.stringify(m)});`)
|
||||
.join('\n');
|
||||
|
||||
return `
|
||||
/**
|
||||
|
@ -28,13 +28,16 @@ window.__KBN__ = {
|
|||
esShardTimeout: 1500,
|
||||
esApiVersion: '2.0',
|
||||
esRequestTimeout: '300000'
|
||||
},
|
||||
uiSettings: {
|
||||
defaults: ${JSON.stringify(env.defaultUiSettings, null, 2).split('\n').join('\n ')},
|
||||
user: {}
|
||||
}
|
||||
};
|
||||
|
||||
require('ui/test_harness');
|
||||
${requires}
|
||||
require('ui/test_harness').bootstrap(/* go! */);
|
||||
|
||||
`;
|
||||
|
||||
};
|
||||
|
|
|
@ -121,6 +121,10 @@ module.exports = () => Joi.object({
|
|||
)
|
||||
.default(Joi.ref('$dev')),
|
||||
profile: Joi.boolean().default(false)
|
||||
}).default(),
|
||||
|
||||
status: Joi.object({
|
||||
allowAnonymous: Joi.boolean().default(false)
|
||||
}).default()
|
||||
|
||||
}).default();
|
||||
|
|
|
@ -32,6 +32,9 @@ module.exports = class KbnServer {
|
|||
// setup this.uiExports and this.bundles
|
||||
require('../ui'),
|
||||
|
||||
// setup server.uiSettings
|
||||
require('../ui/settings'),
|
||||
|
||||
// ensure that all bundles are built, or that the
|
||||
// lazy bundle server is running
|
||||
require('../optimize'),
|
||||
|
|
42
src/server/status/__tests__/wrap_auth_config.js
Normal file
42
src/server/status/__tests__/wrap_auth_config.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
import expect from 'expect.js';
|
||||
import wrapAuthConfig from '../wrap_auth_config';
|
||||
|
||||
describe('Status wrapAuthConfig', () => {
|
||||
let options;
|
||||
|
||||
beforeEach(() => {
|
||||
options = {
|
||||
method: 'GET',
|
||||
path: '/status',
|
||||
handler: function (request, reply) {
|
||||
return reply();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
it('should return a function', () => {
|
||||
expect(wrapAuthConfig()).to.be.a('function');
|
||||
expect(wrapAuthConfig(true)).to.be.a('function');
|
||||
expect(wrapAuthConfig(false)).to.be.a('function');
|
||||
});
|
||||
|
||||
it('should not add auth config by default', () => {
|
||||
const wrapAuth = wrapAuthConfig();
|
||||
const wrapped = wrapAuth(options);
|
||||
expect(wrapped).to.not.have.property('config');
|
||||
});
|
||||
|
||||
it('should not add auth config if allowAnonymous is false', () => {
|
||||
const wrapAuth = wrapAuthConfig(false);
|
||||
const wrapped = wrapAuth(options);
|
||||
expect(wrapped).to.not.have.property('config');
|
||||
});
|
||||
|
||||
it('should add auth config if allowAnonymous is true', () => {
|
||||
const wrapAuth = wrapAuthConfig(true);
|
||||
const wrapped = wrapAuth(options);
|
||||
expect(wrapped).to.have.property('config');
|
||||
expect(wrapped.config).to.have.property('auth');
|
||||
expect(wrapped.config.auth).to.be(false);
|
||||
});
|
||||
});
|
|
@ -1,15 +1,18 @@
|
|||
import _ from 'lodash';
|
||||
import ServerStatus from './server_status';
|
||||
import wrapAuthConfig from './wrap_auth_config';
|
||||
import { join } from 'path';
|
||||
module.exports = function (kbnServer, server, config) {
|
||||
|
||||
export default function (kbnServer, server, config) {
|
||||
kbnServer.status = new ServerStatus(kbnServer.server);
|
||||
|
||||
if (server.plugins.good) {
|
||||
kbnServer.mixin(require('./metrics'));
|
||||
}
|
||||
|
||||
server.route({
|
||||
const wrapAuth = wrapAuthConfig(config.get('status.allowAnonymous'));
|
||||
|
||||
server.route(wrapAuth({
|
||||
method: 'GET',
|
||||
path: '/api/status',
|
||||
handler: function (request, reply) {
|
||||
|
@ -20,20 +23,27 @@ module.exports = function (kbnServer, server, config) {
|
|||
metrics: kbnServer.metrics
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
server.decorate('reply', 'renderStatusPage', async function () {
|
||||
const app = kbnServer.uiExports.getHiddenApp('status_page');
|
||||
const response = await getResponse(this);
|
||||
response.code(kbnServer.status.isGreen() ? 200 : 503);
|
||||
return response;
|
||||
|
||||
function getResponse(ctx) {
|
||||
if (app) {
|
||||
return ctx.renderApp(app);
|
||||
}
|
||||
return ctx(kbnServer.status.toString());
|
||||
}
|
||||
});
|
||||
|
||||
server.decorate('reply', 'renderStatusPage', function () {
|
||||
let app = kbnServer.uiExports.getHiddenApp('status_page');
|
||||
let resp = app ? this.renderApp(app) : this(kbnServer.status.toString());
|
||||
resp.code(kbnServer.status.isGreen() ? 200 : 503);
|
||||
return resp;
|
||||
});
|
||||
|
||||
server.route({
|
||||
server.route(wrapAuth({
|
||||
method: 'GET',
|
||||
path: '/status',
|
||||
handler: function (request, reply) {
|
||||
return reply.renderStatusPage();
|
||||
}
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
|
6
src/server/status/wrap_auth_config.js
Normal file
6
src/server/status/wrap_auth_config.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import {assign, identity} from 'lodash';
|
||||
|
||||
export default (allowAnonymous) => {
|
||||
if (allowAnonymous) return options => assign(options, {config: {auth: false}});
|
||||
return identity;
|
||||
};
|
|
@ -8,7 +8,8 @@ import UiExports from './ui_exports';
|
|||
import UiBundle from './ui_bundle';
|
||||
import UiBundleCollection from './ui_bundle_collection';
|
||||
import UiBundlerEnv from './ui_bundler_env';
|
||||
module.exports = async (kbnServer, server, config) => {
|
||||
|
||||
export default async (kbnServer, server, config) => {
|
||||
|
||||
const loadingGif = readFile(fromRoot('src/ui/public/loading.gif'), { encoding: 'base64'});
|
||||
|
||||
|
@ -59,7 +60,8 @@ module.exports = async (kbnServer, server, config) => {
|
|||
}
|
||||
});
|
||||
|
||||
server.decorate('reply', 'renderApp', function (app) {
|
||||
server.decorate('reply', 'renderApp', async function (app) {
|
||||
const uiSettings = server.uiSettings();
|
||||
const payload = {
|
||||
app: app,
|
||||
nav: uiExports.navLinks.inOrder,
|
||||
|
@ -68,6 +70,10 @@ module.exports = async (kbnServer, server, config) => {
|
|||
buildSha: config.get('pkg.buildSha'),
|
||||
basePath: config.get('server.basePath'),
|
||||
serverName: config.get('server.name'),
|
||||
uiSettings: {
|
||||
defaults: await uiSettings.getDefaults(),
|
||||
user: await uiSettings.getUserProvided()
|
||||
},
|
||||
vars: defaults(app.getInjectedVars() || {}, uiExports.defaultInjectedVars),
|
||||
};
|
||||
|
||||
|
|
|
@ -72,13 +72,13 @@ describe('AggConfig Filters', function () {
|
|||
expect(fieldParams).to.have.property('gte');
|
||||
expect(fieldParams.gte).to.be.a('number');
|
||||
|
||||
expect(fieldParams).to.have.property('lte');
|
||||
expect(fieldParams.lte).to.be.a('number');
|
||||
expect(fieldParams).to.have.property('lt');
|
||||
expect(fieldParams.lt).to.be.a('number');
|
||||
|
||||
expect(fieldParams).to.have.property('format');
|
||||
expect(fieldParams.format).to.be('epoch_millis');
|
||||
|
||||
expect(fieldParams.gte).to.be.lessThan(fieldParams.lte);
|
||||
expect(fieldParams.gte).to.be.lessThan(fieldParams.lt);
|
||||
|
||||
expect(filter).to.have.property('meta');
|
||||
expect(filter.meta).to.have.property('index', vis.indexPattern.id);
|
||||
|
@ -102,7 +102,7 @@ describe('AggConfig Filters', function () {
|
|||
let params = filter.range[field.name];
|
||||
|
||||
expect(params.gte).to.be(+bucketStart);
|
||||
expect(params.lte).to.be(+bucketStart.clone().add(interval).subtract(1, 'ms'));
|
||||
expect(params.lt).to.be(+bucketStart.clone().add(interval));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@ export default function createDateHistogramFilterProvider(Private) {
|
|||
|
||||
return buildRangeFilter(agg.params.field, {
|
||||
gte: start.valueOf(),
|
||||
lte: start.add(interval).subtract(1, 'ms').valueOf(),
|
||||
lt: start.add(interval).valueOf(),
|
||||
format: 'epoch_millis'
|
||||
}, agg.vis.indexPattern);
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import buildRangeFilter from 'ui/filter_manager/lib/range';
|
||||
|
||||
export default function createHistogramFitlerProvider(Private) {
|
||||
export default function createHistogramFilterProvider(Private) {
|
||||
return function (aggConfig, key) {
|
||||
let value = parseInt(key, 10);
|
||||
|
||||
|
|
|
@ -7,21 +7,21 @@ import AggTypesBucketsBucketAggTypeProvider from 'ui/agg_types/buckets/_bucket_a
|
|||
import TimeBucketsProvider from 'ui/time_buckets';
|
||||
import AggTypesBucketsCreateFilterDateHistogramProvider from 'ui/agg_types/buckets/create_filter/date_histogram';
|
||||
import AggTypesBucketsIntervalOptionsProvider from 'ui/agg_types/buckets/_interval_options';
|
||||
import ConfigDefaultsProvider from 'ui/config/defaults';
|
||||
import intervalTemplate from 'ui/agg_types/controls/interval.html';
|
||||
export default function DateHistogramAggType(timefilter, config, Private) {
|
||||
let BucketAggType = Private(AggTypesBucketsBucketAggTypeProvider);
|
||||
let TimeBuckets = Private(TimeBucketsProvider);
|
||||
let createFilter = Private(AggTypesBucketsCreateFilterDateHistogramProvider);
|
||||
let intervalOptions = Private(AggTypesBucketsIntervalOptionsProvider);
|
||||
let configDefaults = Private(ConfigDefaultsProvider);
|
||||
const BucketAggType = Private(AggTypesBucketsBucketAggTypeProvider);
|
||||
const TimeBuckets = Private(TimeBucketsProvider);
|
||||
const createFilter = Private(AggTypesBucketsCreateFilterDateHistogramProvider);
|
||||
const intervalOptions = Private(AggTypesBucketsIntervalOptionsProvider);
|
||||
|
||||
let detectedTimezone = tzDetect.determine().name();
|
||||
let tzOffset = moment().format('Z');
|
||||
const detectedTimezone = tzDetect.determine().name();
|
||||
const tzOffset = moment().format('Z');
|
||||
|
||||
function getInterval(agg) {
|
||||
let interval = _.get(agg, ['params', 'interval']);
|
||||
if (interval && interval.val === 'custom') interval = _.get(agg, ['params', 'customInterval']);
|
||||
const interval = _.get(agg, ['params', 'interval']);
|
||||
if (interval && interval.val === 'custom') {
|
||||
return _.get(agg, ['params', 'customInterval']);
|
||||
}
|
||||
return interval;
|
||||
}
|
||||
|
||||
|
@ -39,8 +39,8 @@ export default function DateHistogramAggType(timefilter, config, Private) {
|
|||
date: true
|
||||
},
|
||||
makeLabel: function (agg) {
|
||||
let output = this.params.write(agg);
|
||||
let params = output.params;
|
||||
const output = this.params.write(agg);
|
||||
const params = output.params;
|
||||
return params.field + ' per ' + (output.metricScaleText || output.bucketInterval.description);
|
||||
},
|
||||
createFilter: createFilter,
|
||||
|
@ -81,7 +81,7 @@ export default function DateHistogramAggType(timefilter, config, Private) {
|
|||
name: 'interval',
|
||||
type: 'optioned',
|
||||
deserialize: function (state) {
|
||||
let interval = _.find(intervalOptions, {val: state});
|
||||
const interval = _.find(intervalOptions, {val: state});
|
||||
return interval || _.find(intervalOptions, function (option) {
|
||||
// For upgrading from 4.0.x to 4.1.x - intervals are now stored as 'y' instead of 'year',
|
||||
// but this maps the old values to the new values
|
||||
|
@ -98,25 +98,26 @@ export default function DateHistogramAggType(timefilter, config, Private) {
|
|||
setBounds(agg);
|
||||
agg.buckets.setInterval(getInterval(agg));
|
||||
|
||||
let interval = agg.buckets.getInterval();
|
||||
const interval = agg.buckets.getInterval();
|
||||
output.bucketInterval = interval;
|
||||
output.params.interval = interval.expression;
|
||||
|
||||
let isDefaultTimezone = config.get('dateFormat:tz') === configDefaults['dateFormat:tz'].value;
|
||||
output.params.time_zone = isDefaultTimezone ?
|
||||
(detectedTimezone || tzOffset) :
|
||||
config.get('dateFormat:tz');
|
||||
|
||||
let scaleMetrics = interval.scaled && interval.scale < 1;
|
||||
if (scaleMetrics) {
|
||||
scaleMetrics = _.every(agg.vis.aggs.bySchemaGroup.metrics, function (agg) {
|
||||
return agg.type && (agg.type.name === 'count' || agg.type.name === 'sum');
|
||||
});
|
||||
const isDefaultTimezone = config.isDefault('dateFormat:tz');
|
||||
if (isDefaultTimezone) {
|
||||
output.params.time_zone = detectedTimezone || tzOffset;
|
||||
} else {
|
||||
output.params.time_zone = config.get('dateFormat:tz');
|
||||
}
|
||||
|
||||
const scaleMetrics = interval.scaled && interval.scale < 1;
|
||||
if (scaleMetrics) {
|
||||
output.metricScale = interval.scale;
|
||||
output.metricScaleText = interval.preScaled.description;
|
||||
const all = _.every(agg.vis.aggs.bySchemaGroup.metrics, function (agg) {
|
||||
return agg.type && (agg.type.name === 'count' || agg.type.name === 'sum');
|
||||
});
|
||||
if (all) {
|
||||
output.metricScale = interval.scale;
|
||||
output.metricScaleText = interval.preScaled.description;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -140,7 +141,7 @@ export default function DateHistogramAggType(timefilter, config, Private) {
|
|||
name: 'extended_bounds',
|
||||
default: {},
|
||||
write: function (agg, output) {
|
||||
let val = agg.params.extended_bounds;
|
||||
const val = agg.params.extended_bounds;
|
||||
|
||||
if (val.min != null || val.max != null) {
|
||||
output.params.extended_bounds = {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import _ from 'lodash';
|
||||
export default function BoundToConfigObjProvider($rootScope, config) {
|
||||
export default function BoundToConfigObjProvider(config) {
|
||||
|
||||
/**
|
||||
* Create an object with properties that may be bound to config values.
|
||||
|
@ -18,21 +18,17 @@ export default function BoundToConfigObjProvider($rootScope, config) {
|
|||
function BoundToConfigObj(input) {
|
||||
const self = this;
|
||||
|
||||
_.forOwn(input, function (val, prop) {
|
||||
if (!_.isString(val) || val.charAt(0) !== '=') {
|
||||
self[prop] = val;
|
||||
_.forOwn(input, function (value, prop) {
|
||||
if (!_.isString(value) || value.charAt(0) !== '=') {
|
||||
self[prop] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
const configKey = val.substr(1);
|
||||
|
||||
update();
|
||||
$rootScope.$on('init:config', update);
|
||||
$rootScope.$on('change:config.' + configKey, update);
|
||||
function update() {
|
||||
self[prop] = config.get(configKey);
|
||||
}
|
||||
const configKey = value.substr(1);
|
||||
|
||||
config.watch(configKey, function update(value) {
|
||||
self[prop] = value;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
1
src/ui/public/chrome/api/angular.js
vendored
1
src/ui/public/chrome/api/angular.js
vendored
|
@ -24,6 +24,7 @@ module.exports = function (chrome, internals) {
|
|||
.value('buildNum', internals.buildNum)
|
||||
.value('buildSha', internals.buildSha)
|
||||
.value('serverName', internals.serverName)
|
||||
.value('uiSettings', internals.uiSettings)
|
||||
.value('sessionId', Date.now())
|
||||
.value('chrome', chrome)
|
||||
.value('esUrl', (function () {
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import ConfigDefaultsProvider from 'ui/config/defaults';
|
||||
|
||||
describe('config component', function () {
|
||||
let $scope;
|
||||
let config;
|
||||
let defaults;
|
||||
let $scope;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function ($injector, Private) {
|
||||
config = $injector.get('config');
|
||||
$scope = $injector.get('$rootScope');
|
||||
defaults = Private(ConfigDefaultsProvider);
|
||||
}));
|
||||
|
||||
describe('#get', function () {
|
||||
|
@ -18,22 +16,15 @@ describe('config component', function () {
|
|||
it('gives access to config values', function () {
|
||||
expect(config.get('dateFormat')).to.be.a('string');
|
||||
});
|
||||
|
||||
it('reads from the defaults', function () {
|
||||
let initial = config.get('dateFormat');
|
||||
let newDefault = initial + '- new';
|
||||
defaults.dateFormat.value = newDefault;
|
||||
expect(config.get('dateFormat')).to.be(newDefault);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#set', function () {
|
||||
|
||||
it('stores a value in the config val set', function () {
|
||||
let initial = config.get('dateFormat');
|
||||
const original = config.get('dateFormat');
|
||||
config.set('dateFormat', 'notaformat');
|
||||
expect(config.get('dateFormat')).to.be('notaformat');
|
||||
config.set('dateFormat', original);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -41,26 +32,27 @@ describe('config component', function () {
|
|||
describe('#$bind', function () {
|
||||
|
||||
it('binds a config key to a $scope property', function () {
|
||||
let dateFormat = config.get('dateFormat');
|
||||
config.$bind($scope, 'dateFormat');
|
||||
const dateFormat = config.get('dateFormat');
|
||||
config.bindToScope($scope, 'dateFormat');
|
||||
expect($scope).to.have.property('dateFormat', dateFormat);
|
||||
});
|
||||
|
||||
it('alows overriding the property name', function () {
|
||||
let dateFormat = config.get('dateFormat');
|
||||
config.$bind($scope, 'dateFormat', 'defaultDateFormat');
|
||||
const dateFormat = config.get('dateFormat');
|
||||
config.bindToScope($scope, 'dateFormat', 'defaultDateFormat');
|
||||
expect($scope).to.not.have.property('dateFormat');
|
||||
expect($scope).to.have.property('defaultDateFormat', dateFormat);
|
||||
});
|
||||
|
||||
it('keeps the property up to date', function () {
|
||||
let dateFormat = config.get('dateFormat');
|
||||
let newDateFormat = dateFormat + ' NEW NEW NEW!';
|
||||
config.$bind($scope, 'dateFormat');
|
||||
const original = config.get('dateFormat');
|
||||
const newDateFormat = original + ' NEW NEW NEW!';
|
||||
config.bindToScope($scope, 'dateFormat');
|
||||
|
||||
expect($scope).to.have.property('dateFormat', dateFormat);
|
||||
expect($scope).to.have.property('dateFormat', original);
|
||||
config.set('dateFormat', newDateFormat);
|
||||
expect($scope).to.have.property('dateFormat', newDateFormat);
|
||||
config.set('dateFormat', original);
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -1,87 +1,68 @@
|
|||
import _ from 'lodash';
|
||||
import angular from 'angular';
|
||||
import ConfigValsProvider from 'ui/config/_vals';
|
||||
import Notifier from 'ui/notify/notifier';
|
||||
export default function DelayedUpdaterFactory($http, chrome, Promise) {
|
||||
let unsavedChanges = {};
|
||||
let unresolvedPromises = [];
|
||||
let saveTimeout = null;
|
||||
|
||||
export default function DelayedUpdaterFactory(Private, $rootScope, Promise) {
|
||||
let notify = new Notifier();
|
||||
return function delayedUpdate(key, value) {
|
||||
unsavedChanges[key] = value;
|
||||
|
||||
let vals = Private(ConfigValsProvider);
|
||||
|
||||
return function DelayedUpdater(doc) {
|
||||
let updater = this;
|
||||
let queue = [];
|
||||
let log = {};
|
||||
let timer;
|
||||
|
||||
updater.fire = function () {
|
||||
clearTimeout(timer);
|
||||
|
||||
// only fire once
|
||||
if (updater.fired) return;
|
||||
updater.fired = true;
|
||||
|
||||
let method;
|
||||
let body;
|
||||
let updated = [];
|
||||
let deleted = [];
|
||||
|
||||
// seperate the log into lists
|
||||
Object.keys(log).forEach(function (key) {
|
||||
if (log[key] === 'updated') updated.push(key);
|
||||
else deleted.push(key);
|
||||
});
|
||||
|
||||
if (deleted.length) {
|
||||
method = 'doIndex';
|
||||
body = _.clone(vals);
|
||||
} else {
|
||||
method = 'doUpdate';
|
||||
body = _.pick(vals, updated);
|
||||
}
|
||||
|
||||
doc[method](vals)
|
||||
.then(
|
||||
function (resp) {
|
||||
queue.forEach(function (q) { q.resolve(resp); });
|
||||
},
|
||||
function (err) {
|
||||
queue.forEach(function (q) { q.reject(err); });
|
||||
}
|
||||
)
|
||||
.finally(function () {
|
||||
$rootScope.$broadcast('change:config', updated.concat(deleted));
|
||||
});
|
||||
};
|
||||
|
||||
updater.update = function (key, val, silentAndLocal) {
|
||||
let newVal = val;
|
||||
let oldVal = vals[key];
|
||||
|
||||
if (angular.equals(newVal, oldVal)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
else if (newVal == null) {
|
||||
delete vals[key];
|
||||
log[key] = 'deleted';
|
||||
}
|
||||
else {
|
||||
vals[key] = newVal;
|
||||
log[key] = 'updated';
|
||||
}
|
||||
|
||||
if (silentAndLocal) return Promise.resolve();
|
||||
|
||||
let defer = Promise.defer();
|
||||
queue.push(defer);
|
||||
notify.log('config change: ' + key + ': ' + oldVal + ' -> ' + newVal);
|
||||
$rootScope.$broadcast('change:config.' + key, newVal, oldVal);
|
||||
|
||||
// reset the fire timer
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(updater.fire, 200);
|
||||
return defer.promise;
|
||||
};
|
||||
return new Promise(saveSoon)
|
||||
.then(res => res.data.settings);
|
||||
};
|
||||
|
||||
function saveSoon(resolve, reject) {
|
||||
if (saveTimeout) {
|
||||
clearTimeout(saveTimeout);
|
||||
}
|
||||
|
||||
saveTimeout = setTimeout(fire, 200);
|
||||
unresolvedPromises.push({ resolve, reject });
|
||||
}
|
||||
|
||||
function fire() {
|
||||
const changes = unsavedChanges;
|
||||
const promises = unresolvedPromises;
|
||||
|
||||
unresolvedPromises = [];
|
||||
unsavedChanges = {};
|
||||
|
||||
persist(changes)
|
||||
.then(result => settle(promises, `resolve`, result))
|
||||
.catch(reason => settle(promises, `reject`, reason));
|
||||
}
|
||||
|
||||
function settle(listeners, decision, data) {
|
||||
listeners.forEach(listener => listener[decision](data));
|
||||
}
|
||||
|
||||
function persist(changes) {
|
||||
const keys = Object.keys(changes);
|
||||
if (keys.length === 1) {
|
||||
const [key] = keys;
|
||||
const value = changes[key];
|
||||
const update = value === null ? remove : edit;
|
||||
return update(key, value);
|
||||
}
|
||||
return editMany(changes);
|
||||
}
|
||||
|
||||
function remove(key) {
|
||||
return sync(`delete`, { postfix: `/${key}` });
|
||||
}
|
||||
|
||||
function edit(key, value) {
|
||||
return sync(`post`, { postfix: `/${key}`, data: { value } });
|
||||
}
|
||||
|
||||
function editMany(changes) {
|
||||
return sync(`post`, { data: { changes } });
|
||||
}
|
||||
|
||||
function sync(method, { postfix = '', data } = {}) {
|
||||
return $http({
|
||||
method,
|
||||
url: chrome.addBasePath(`/api/kibana/settings${postfix}`),
|
||||
data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
export default function ConfigValsService() {
|
||||
return {};
|
||||
};
|
|
@ -1,124 +1,27 @@
|
|||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import ConfigDefaultsProvider from 'ui/config/defaults';
|
||||
import ConfigDelayedUpdaterProvider from 'ui/config/_delayed_updater';
|
||||
import ConfigValsProvider from 'ui/config/_vals';
|
||||
import DocSourceProvider from 'ui/courier/data_source/doc_source';
|
||||
import { once, cloneDeep, defaultsDeep, isPlainObject } from 'lodash';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import uiModules from 'ui/modules';
|
||||
import Notifier from 'ui/notify/notifier';
|
||||
|
||||
let module = uiModules.get('kibana/config');
|
||||
|
||||
uiRoutes.addSetupWork(function (config) {
|
||||
return config.init();
|
||||
});
|
||||
import ConfigDelayedUpdaterProvider from 'ui/config/_delayed_updater';
|
||||
const module = uiModules.get('kibana/config');
|
||||
|
||||
// service for delivering config variables to everywhere else
|
||||
module.service('config', function (Private, kbnVersion, kbnIndex, $rootScope, buildNum) {
|
||||
let config = this;
|
||||
module.service(`config`, function (Private, $rootScope, $http, chrome, uiSettings) {
|
||||
const config = this;
|
||||
const notify = new Notifier({ location: `Config` });
|
||||
const { defaults, user: initialUserSettings } = uiSettings;
|
||||
const delayedUpdate = Private(ConfigDelayedUpdaterProvider);
|
||||
let settings = mergeSettings(defaults, initialUserSettings);
|
||||
|
||||
let defaults = Private(ConfigDefaultsProvider);
|
||||
let DelayedUpdater = Private(ConfigDelayedUpdaterProvider);
|
||||
let vals = Private(ConfigValsProvider);
|
||||
|
||||
let notify = new Notifier({
|
||||
location: 'Config'
|
||||
});
|
||||
|
||||
// active or previous instance of DelayedUpdater. This will log and then process an
|
||||
// update once it is requested by calling #set() or #clear().
|
||||
let updater;
|
||||
|
||||
let DocSource = Private(DocSourceProvider);
|
||||
let doc = (new DocSource())
|
||||
.index(kbnIndex)
|
||||
.type('config')
|
||||
.id(kbnVersion);
|
||||
|
||||
/******
|
||||
* PUBLIC API
|
||||
******/
|
||||
|
||||
/**
|
||||
* Executes once and returns a promise that is resolved once the
|
||||
* config has loaded for the first time.
|
||||
*
|
||||
* @return {Promise} - Resolved when the config loads initially
|
||||
*/
|
||||
config.init = _.once(function () {
|
||||
let complete = notify.lifecycle('config init');
|
||||
|
||||
return (function getDoc() {
|
||||
|
||||
// used to apply an entire es response to the vals, silentAndLocal will prevent
|
||||
// event/notifications/writes from occuring.
|
||||
let applyMassUpdate = function (resp, silentAndLocal) {
|
||||
_.union(_.keys(resp._source), _.keys(vals)).forEach(function (key) {
|
||||
change(key, resp._source[key], silentAndLocal);
|
||||
});
|
||||
};
|
||||
|
||||
return doc.fetch().then(function initDoc(resp) {
|
||||
if (!resp.found) {
|
||||
return doc.doIndex({
|
||||
buildNum: buildNum
|
||||
}).then(getDoc);
|
||||
} else {
|
||||
// apply update, and keep it quiet the first time
|
||||
applyMassUpdate(resp, true);
|
||||
|
||||
// don't keep it quiet other times
|
||||
doc.onUpdate(function (resp) {
|
||||
applyMassUpdate(resp, false);
|
||||
});
|
||||
}
|
||||
});
|
||||
}())
|
||||
.then(function () {
|
||||
$rootScope.$broadcast('init:config');
|
||||
})
|
||||
.then(complete, complete.failure);
|
||||
});
|
||||
|
||||
config.get = function (key, defaultVal) {
|
||||
let keyVal;
|
||||
|
||||
if (vals[key] == null) {
|
||||
if (defaultVal == null) {
|
||||
keyVal = defaults[key].value;
|
||||
} else {
|
||||
keyVal = _.cloneDeep(defaultVal);
|
||||
}
|
||||
} else {
|
||||
keyVal = vals[key];
|
||||
}
|
||||
|
||||
if (defaults[key] && defaults[key].type === 'json') {
|
||||
return JSON.parse(keyVal);
|
||||
}
|
||||
return keyVal;
|
||||
};
|
||||
|
||||
// sets a value in the config
|
||||
config.set = function (key, val) {
|
||||
if (_.isPlainObject(val)) {
|
||||
return change(key, angular.toJson(val));
|
||||
} else {
|
||||
return change(key, val);
|
||||
}
|
||||
};
|
||||
|
||||
// clears a value from the config
|
||||
config.clear = function (key) {
|
||||
return change(key);
|
||||
};
|
||||
// alias for clear
|
||||
config.delete = config.clear;
|
||||
|
||||
config.close = function () {
|
||||
if (updater) updater.fire();
|
||||
};
|
||||
config.getAll = () => cloneDeep(settings);
|
||||
config.get = key => getCurrentValue(key);
|
||||
config.set = (key, val) => change(key, isPlainObject(val) ? angular.toJson(val) : val);
|
||||
config.remove = key => change(key, null);
|
||||
config.isDefault = key => !(key in settings) || nullOrEmpty(settings[key].userValue);
|
||||
config.isCustom = key => key in settings && !('value' in settings[key]);
|
||||
config.watchAll = (fn, scope) => watchAll(scope, fn);
|
||||
config.watch = (key, fn, scope) => watch(key, scope, fn);
|
||||
|
||||
/**
|
||||
* A little helper for binding config variables to $scopes
|
||||
|
@ -129,34 +32,94 @@ module.service('config', function (Private, kbnVersion, kbnIndex, $rootScope, bu
|
|||
* be stored. Defaults to the config key
|
||||
* @return {function} - an unbind function
|
||||
*/
|
||||
config.$bind = function ($scope, key, property) {
|
||||
if (!property) property = key;
|
||||
|
||||
let update = function () {
|
||||
$scope[property] = config.get(key);
|
||||
};
|
||||
|
||||
update();
|
||||
return _.partial(_.invoke, [
|
||||
$scope.$on('change:config.' + key, update),
|
||||
$scope.$on('init:config', update)
|
||||
], 'call');
|
||||
config.bindToScope = function (scope, key, property = key) {
|
||||
return watch(key, scope, update);
|
||||
function update(newVal) {
|
||||
scope[property] = newVal;
|
||||
}
|
||||
};
|
||||
|
||||
/*****
|
||||
* PRIVATE API
|
||||
*****/
|
||||
function change(key, val, silentAndLocal) {
|
||||
// if the previous updater has already fired, then start over with null
|
||||
if (updater && updater.fired) updater = null;
|
||||
// create a new updater
|
||||
if (!updater) updater = new DelayedUpdater(doc);
|
||||
// return a promise that will be resolved once the action is eventually done
|
||||
return updater.update(key, val, silentAndLocal);
|
||||
function watch(key, scope = $rootScope, fn) {
|
||||
if (!(key in settings)) {
|
||||
throw new Error(`Unexpected \`config.watch("${key}", fn)\` call on unrecognized configuration setting "${key}".
|
||||
Setting an initial value via \`config.set("${key}", value)\` before binding
|
||||
any custom setting configuration watchers for "${key}" may fix this issue.`);
|
||||
}
|
||||
const newVal = config.get(key);
|
||||
const update = (e, ...args) => fn(...args);
|
||||
fn(newVal, null, key, config);
|
||||
return scope.$on(`change:config.${key}`, update);
|
||||
}
|
||||
|
||||
config._vals = function () {
|
||||
return _.cloneDeep(vals);
|
||||
};
|
||||
function watchAll(scope = $rootScope, fn) {
|
||||
const update = (e, ...args) => fn(...args);
|
||||
fn(null, null, null, config);
|
||||
return scope.$on(`change:config`, update);
|
||||
}
|
||||
|
||||
function change(key, value) {
|
||||
const oldVal = key in settings ? settings[key].userValue : undefined;
|
||||
const newVal = key in defaults && defaults[key].defaultValue === value ? null : value;
|
||||
const unchanged = oldVal === newVal;
|
||||
if (unchanged) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
const initialVal = config.get(key);
|
||||
localUpdate(key, newVal);
|
||||
|
||||
return delayedUpdate(key, newVal)
|
||||
.then(updatedSettings => {
|
||||
settings = mergeSettings(defaults, updatedSettings);
|
||||
})
|
||||
.catch(reason => {
|
||||
localUpdate(key, initialVal);
|
||||
notify.error(reason);
|
||||
});
|
||||
}
|
||||
|
||||
function localUpdate(key, newVal) {
|
||||
const oldVal = config.get(key);
|
||||
patch(key, newVal);
|
||||
advertise(key, oldVal);
|
||||
}
|
||||
|
||||
function patch(key, value) {
|
||||
if (!(key in settings)) {
|
||||
settings[key] = {};
|
||||
}
|
||||
if (value === null) {
|
||||
delete settings[key].userValue;
|
||||
} else {
|
||||
settings[key].userValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
function advertise(key, oldVal) {
|
||||
const newVal = config.get(key);
|
||||
notify.log(`config change: ${key}: ${oldVal} -> ${newVal}`);
|
||||
$rootScope.$broadcast(`change:config.${key}`, newVal, oldVal, key, config);
|
||||
$rootScope.$broadcast(`change:config`, newVal, oldVal, key, config);
|
||||
}
|
||||
|
||||
function nullOrEmpty(value) {
|
||||
return value === undefined || value === null;
|
||||
}
|
||||
|
||||
function getCurrentValue(key) {
|
||||
if (!(key in settings)) {
|
||||
throw new Error(`Unexpected \`config.get("${key}")\` call on unrecognized configuration setting "${key}".
|
||||
Setting an initial value via \`config.set("${key}", value)\` before attempting to retrieve
|
||||
any custom setting value for "${key}" may fix this issue.`);
|
||||
}
|
||||
const { userValue, value: defaultValue, type } = settings[key];
|
||||
const currentValue = config.isDefault(key) ? defaultValue : userValue;
|
||||
if (type === 'json') {
|
||||
return JSON.parse(currentValue);
|
||||
}
|
||||
return currentValue;
|
||||
}
|
||||
});
|
||||
|
||||
function mergeSettings(extended, defaults) {
|
||||
return defaultsDeep(extended, defaults);
|
||||
}
|
||||
|
|
|
@ -310,7 +310,7 @@ export default function SourceAbstractFactory(Private, Promise, PromiseEmitter)
|
|||
|
||||
/**
|
||||
* Translate a filter into a query to support es 3+
|
||||
* @param {Object} filter - The fitler to translate
|
||||
* @param {Object} filter - The filter to translate
|
||||
* @return {Object} the query version of that filter
|
||||
*/
|
||||
let translateToQuery = function (filter) {
|
||||
|
|
|
@ -128,7 +128,7 @@ describe('typeahead directive', function () {
|
|||
expect($typeaheadScope.items.length).to.be(typeaheadItems.length);
|
||||
});
|
||||
|
||||
it('should order fitlered results', function () {
|
||||
it('should order filtered results', function () {
|
||||
let entries = ['ac/dc', 'anthrax', 'abba', 'phantogram', 'skrillex'];
|
||||
let allEntries = typeaheadItems.concat(entries);
|
||||
let startMatches = allEntries.filter(function (item) {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import $ from 'jquery';
|
||||
import 'ui/filters/short_dots';
|
||||
import uiModules from 'ui/modules';
|
||||
let module = uiModules.get('kibana');
|
||||
|
@ -52,11 +53,14 @@ module.directive('fieldName', function ($compile, $rootScope, $filter) {
|
|||
let displayName = $filter('shortDots')(name);
|
||||
|
||||
$el
|
||||
.text(displayName)
|
||||
.attr('title', name)
|
||||
.toggleClass('no-results', results)
|
||||
.toggleClass('scripted', scripted)
|
||||
.prepend(typeIcon(type));
|
||||
.prepend(typeIcon(type))
|
||||
.append($('<span>')
|
||||
.text(displayName)
|
||||
.addClass('discover-field-name')
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -55,7 +55,7 @@ export default function mapFilterProvider(Promise, Private) {
|
|||
/**
|
||||
* Map the filter into an object with the key and value exposed so it's
|
||||
* easier to work with in the template
|
||||
* @param {object} fitler The filter the map
|
||||
* @param {object} filter The filter the map
|
||||
* @returns {Promise}
|
||||
*/
|
||||
return function (filter) {
|
||||
|
|
|
@ -6,7 +6,7 @@ uiModules
|
|||
.get('kibana')
|
||||
.filter('moment', function (config) {
|
||||
return function (datetime) {
|
||||
let format = config.get('dateFormat');
|
||||
const format = config.get('dateFormat');
|
||||
if (moment.isMoment(datetime)) return datetime.format(format);
|
||||
if (_.isDate(datetime)) return moment(datetime).format(format);
|
||||
return datetime;
|
||||
|
|
|
@ -10,20 +10,19 @@ uiModules
|
|||
return Private(shortDotsFilterProvider);
|
||||
});
|
||||
|
||||
function shortDotsFilterProvider(config, $rootScope) {
|
||||
function shortDotsFilterProvider(config) {
|
||||
let filter;
|
||||
|
||||
function updateFilter() {
|
||||
filter = config.get('shortDots:enable') ? _.shortenDottedString : _.identity;
|
||||
config.watch('shortDots:enable', updateFilter);
|
||||
|
||||
return wrapper;
|
||||
|
||||
function updateFilter(enabled) {
|
||||
filter = enabled ? _.shortenDottedString : _.identity;
|
||||
}
|
||||
|
||||
updateFilter();
|
||||
$rootScope.$on('change:config.shortDots:enable', updateFilter);
|
||||
$rootScope.$on('init:config', updateFilter);
|
||||
|
||||
return function (str) {
|
||||
function wrapper(str) {
|
||||
return filter(str);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default shortDotsFilterProvider;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import _ from 'lodash';
|
||||
// Takes a hit, merges it with any stored/scripted fields, and with the metaFields
|
||||
// returns a flattened version
|
||||
export default function FlattenHitProvider(config, $rootScope) {
|
||||
|
||||
export default function FlattenHitProvider(config) {
|
||||
let metaFields = config.get('metaFields');
|
||||
$rootScope.$on('change:config.metaFields', function () {
|
||||
metaFields = config.get('metaFields');
|
||||
|
||||
config.watch('metaFields', value => {
|
||||
metaFields = value;
|
||||
});
|
||||
|
||||
function flattenHit(indexPattern, hit) {
|
||||
|
@ -47,7 +47,7 @@ export default function FlattenHitProvider(config, $rootScope) {
|
|||
return flat;
|
||||
}
|
||||
|
||||
return function (indexPattern) {
|
||||
return function flattenHitWrapper(indexPattern) {
|
||||
function cachedFlatten(hit) {
|
||||
return hit.$$_flattened || (hit.$$_flattened = flattenHit(indexPattern, hit));
|
||||
}
|
||||
|
|
|
@ -13,8 +13,7 @@ import IndexPatternsFieldListProvider from 'ui/index_patterns/_field_list';
|
|||
import IndexPatternsFlattenHitProvider from 'ui/index_patterns/_flatten_hit';
|
||||
import IndexPatternsCalculateIndicesProvider from 'ui/index_patterns/_calculate_indices';
|
||||
import IndexPatternsPatternCacheProvider from 'ui/index_patterns/_pattern_cache';
|
||||
export default function IndexPatternFactory(Private, timefilter, Notifier, config, kbnIndex, Promise, $rootScope, safeConfirm) {
|
||||
|
||||
export default function IndexPatternFactory(Private, timefilter, Notifier, config, kbnIndex, Promise, safeConfirm) {
|
||||
let fieldformats = Private(RegistryFieldFormatsProvider);
|
||||
let getIds = Private(IndexPatternsGetIdsProvider);
|
||||
let mapper = Private(IndexPatternsMapperProvider);
|
||||
|
@ -76,8 +75,11 @@ export default function IndexPatternFactory(Private, timefilter, Notifier, confi
|
|||
.id(self.id);
|
||||
|
||||
// listen for config changes and update field list
|
||||
$rootScope.$on('change:config', function () {
|
||||
initFields();
|
||||
config.watchAll(() => {
|
||||
if (self.fields) {
|
||||
// re-init fields when config changes, but only if we already had fields
|
||||
initFields();
|
||||
}
|
||||
});
|
||||
|
||||
return mappingSetup.isDefined(type)
|
||||
|
|
|
@ -20,10 +20,7 @@ module.exports = function (opts) {
|
|||
let rootSearchSource = Private(CourierDataSourceRootSearchSourceProvider);
|
||||
let path = _.get($route, 'current.$$route.originalPath');
|
||||
|
||||
return config.init()
|
||||
.then(function () {
|
||||
return getIds();
|
||||
})
|
||||
return getIds()
|
||||
.then(function (patterns) {
|
||||
let defaultId = config.get('defaultIndex');
|
||||
let defined = !!defaultId;
|
||||
|
@ -31,7 +28,7 @@ module.exports = function (opts) {
|
|||
let required = !notRequiredRe || !path.match(notRequiredRe);
|
||||
|
||||
if (defined && !exists) {
|
||||
config.clear('defaultIndex');
|
||||
config.remove('defaultIndex');
|
||||
defaultId = defined = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -119,6 +119,8 @@ function add(notif, cb) {
|
|||
return notif;
|
||||
}
|
||||
|
||||
Notifier.prototype.add = add;
|
||||
|
||||
function formatInfo() {
|
||||
let info = [];
|
||||
|
||||
|
|
|
@ -31,21 +31,14 @@ module.run(function ($interval) {
|
|||
// expect access to config (since it's dependent on kibana)
|
||||
if (!!kbnIndex) {
|
||||
require('ui/config');
|
||||
module.run(function ($rootScope, config) {
|
||||
let configInitListener = $rootScope.$on('init:config', function () {
|
||||
applyConfig();
|
||||
configInitListener();
|
||||
});
|
||||
|
||||
$rootScope.$on('change:config', applyConfig);
|
||||
|
||||
function applyConfig() {
|
||||
module.run(function (config) {
|
||||
config.watchAll(() => {
|
||||
Notifier.applyConfig({
|
||||
errorLifetime: config.get('notifications:lifetime:error'),
|
||||
warningLifetime: config.get('notifications:lifetime:warning'),
|
||||
infoLifetime: config.get('notifications:lifetime:info')
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -6,14 +6,12 @@ export default uiRegistry({
|
|||
index: ['id'],
|
||||
group: ['fieldType'],
|
||||
|
||||
constructor: function (config, $rootScope) {
|
||||
constructor: function (config) {
|
||||
let self = this;
|
||||
let defaultMap;
|
||||
|
||||
function init() {
|
||||
parseDefaultTypeMap();
|
||||
$rootScope.$on('init:config', parseDefaultTypeMap);
|
||||
$rootScope.$on('change:config.format:defaultTypeMap', parseDefaultTypeMap);
|
||||
config.watch('format:defaultTypeMap', parseDefaultTypeMap);
|
||||
}
|
||||
|
||||
|
||||
|
@ -73,8 +71,8 @@ export default uiRegistry({
|
|||
});
|
||||
|
||||
|
||||
function parseDefaultTypeMap() {
|
||||
defaultMap = config.get('format:defaultTypeMap');
|
||||
function parseDefaultTypeMap(value) {
|
||||
defaultMap = value;
|
||||
_.forOwn(self, function (fn) {
|
||||
if (_.isFunction(fn) && fn.cache) {
|
||||
// clear all memoize caches
|
||||
|
|
|
@ -8,7 +8,7 @@ let fieldFormats;
|
|||
let FieldFormat;
|
||||
let config;
|
||||
|
||||
let formatIds = [
|
||||
const formatIds = [
|
||||
'bytes',
|
||||
'date',
|
||||
'duration',
|
||||
|
@ -73,7 +73,7 @@ module.exports = describe('conformance', function () {
|
|||
basicPatternTests('number', require('numeral'))();
|
||||
|
||||
it('tries to parse strings', function () {
|
||||
let number = new (fieldFormats.getType('number'))({ pattern: '0.0b' });
|
||||
const number = new (fieldFormats.getType('number'))({ pattern: '0.0b' });
|
||||
expect(number.convert(123.456)).to.be('123.5B');
|
||||
expect(number.convert('123.456')).to.be('123.5B');
|
||||
});
|
||||
|
@ -81,11 +81,11 @@ module.exports = describe('conformance', function () {
|
|||
});
|
||||
|
||||
function basicPatternTests(id, lib) {
|
||||
let confKey = id === 'date' ? 'dateFormat' : 'format:' + id + ':defaultPattern';
|
||||
const confKey = id === 'date' ? 'dateFormat' : 'format:' + id + ':defaultPattern';
|
||||
|
||||
return function () {
|
||||
it('converts using the format:' + id + ':defaultPattern config', function () {
|
||||
let inst = fieldFormats.getInstance(id);
|
||||
const inst = fieldFormats.getInstance(id);
|
||||
[
|
||||
'0b',
|
||||
'0 b',
|
||||
|
@ -93,26 +93,31 @@ module.exports = describe('conformance', function () {
|
|||
'0.[000]b',
|
||||
'0.[0]b'
|
||||
].forEach(function (pattern) {
|
||||
let num = _.random(-10000, 10000, true);
|
||||
const original = config.get(confKey);
|
||||
const num = _.random(-10000, 10000, true);
|
||||
config.set(confKey, pattern);
|
||||
expect(inst.convert(num)).to.be(lib(num).format(pattern));
|
||||
config.set(confKey, original);
|
||||
});
|
||||
});
|
||||
|
||||
it('uses the pattern param if available', function () {
|
||||
let num = _.random(-10000, 10000, true);
|
||||
let defFormat = '0b';
|
||||
let customFormat = '0.00000%';
|
||||
const original = config.get(confKey);
|
||||
const num = _.random(-10000, 10000, true);
|
||||
const defFormat = '0b';
|
||||
const customFormat = '0.00000%';
|
||||
|
||||
config.set(confKey, defFormat);
|
||||
let defInst = fieldFormats.getInstance(id);
|
||||
const defInst = fieldFormats.getInstance(id);
|
||||
|
||||
let Type = fieldFormats.getType(id);
|
||||
let customInst = new Type({ pattern: customFormat });
|
||||
const Type = fieldFormats.getType(id);
|
||||
const customInst = new Type({ pattern: customFormat });
|
||||
|
||||
expect(defInst.convert(num)).to.not.be(customInst.convert(num));
|
||||
expect(defInst.convert(num)).to.be(lib(num).format(defFormat));
|
||||
expect(customInst.convert(num)).to.be(lib(num).format(customFormat));
|
||||
|
||||
config.set(confKey, original);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,16 +2,20 @@ import expect from 'expect.js';
|
|||
import ngMock from 'ng_mock';
|
||||
import RegistryFieldFormatsProvider from 'ui/registry/field_formats';
|
||||
describe('IP Address Format', function () {
|
||||
let fieldFormats;
|
||||
|
||||
let ip;
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function (Private) {
|
||||
fieldFormats = Private(RegistryFieldFormatsProvider);
|
||||
const fieldFormats = Private(RegistryFieldFormatsProvider);
|
||||
ip = fieldFormats.getInstance('ip');
|
||||
}));
|
||||
|
||||
it('converts a value from a decimal to a string', function () {
|
||||
let ip = fieldFormats.getInstance('ip');
|
||||
expect(ip.convert(1186489492)).to.be('70.184.100.148');
|
||||
});
|
||||
|
||||
it('converts null and undefined to -', function () {
|
||||
expect(ip.convert(null)).to.be('-');
|
||||
expect(ip.convert(undefined)).to.be('-');
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -13,6 +13,7 @@ export default function IpFormatProvider(Private) {
|
|||
Ip.fieldType = 'ip';
|
||||
|
||||
Ip.prototype._convert = function (val) {
|
||||
if (val === undefined || val === null) return '-';
|
||||
if (!isFinite(val)) return val;
|
||||
|
||||
// shazzam!
|
||||
|
|
|
@ -2,7 +2,7 @@ import modules from 'ui/modules';
|
|||
|
||||
modules.get('kibana').config(function ($provide) {
|
||||
$provide.decorator('timefilter', function ($delegate) {
|
||||
$delegate.consumeDefaults();
|
||||
$delegate.init();
|
||||
return $delegate;
|
||||
});
|
||||
});
|
||||
|
|
|
@ -38,11 +38,6 @@ uiModules
|
|||
self.enabled = false;
|
||||
|
||||
self.init = _.once(function () {
|
||||
return config.init()
|
||||
.then(self.consumeDefaults);
|
||||
});
|
||||
|
||||
self.consumeDefaults = _.once(function () {
|
||||
let timeDefaults = config.get('timepicker:timeDefaults');
|
||||
let refreshIntervalDefaults = config.get('timepicker:refreshIntervalDefaults');
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ UiModules
|
|||
return {
|
||||
template: toggleHtml,
|
||||
link: ($scope, $el, attrs) => {
|
||||
config.$bind($scope, 'dateFormat:dow', 'dateFormat_dow');
|
||||
config.bindToScope($scope, 'dateFormat:dow', 'dateFormat_dow');
|
||||
$scope.$watch('dateFormat_dow', function (day) {
|
||||
const dow = moment.weekdays().indexOf(day);
|
||||
moment.locale(moment.locale(), { week: { dow } });
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import moment from 'moment-timezone';
|
||||
import _ from 'lodash';
|
||||
|
||||
export default function configDefaultsProvider() {
|
||||
export default function defaultSettingsProvider() {
|
||||
const weekdays = moment.weekdays().slice();
|
||||
const [defaultWeekday] = weekdays;
|
||||
|
||||
|
@ -28,25 +27,26 @@ export default function configDefaultsProvider() {
|
|||
value: 'Browser',
|
||||
description: 'Which timezone should be used. "Browser" will use the timezone detected by your browser.',
|
||||
type: 'select',
|
||||
options: _.union(['Browser'], moment.tz.names())
|
||||
options: ['Browser', ...moment.tz.names()]
|
||||
},
|
||||
'dateFormat:scaled': {
|
||||
type: 'json',
|
||||
value:
|
||||
'[\n' +
|
||||
' ["", "HH:mm:ss.SSS"],\n' +
|
||||
' ["PT1S", "HH:mm:ss"],\n' +
|
||||
' ["PT1M", "HH:mm"],\n' +
|
||||
' ["PT1H",\n' +
|
||||
' "YYYY-MM-DD HH:mm"],\n' +
|
||||
' ["P1DT", "YYYY-MM-DD"],\n' +
|
||||
' ["P1YT", "YYYY"]\n' +
|
||||
']',
|
||||
description: 'Values that define the format used in situations where timebased' +
|
||||
' data is rendered in order, and formatted timestamps should adapt to the' +
|
||||
' interval between measurements. Keys are' +
|
||||
' <a href="http://en.wikipedia.org/wiki/ISO_8601#Time_intervals" target="_blank">' +
|
||||
'ISO8601 intervals.</a>'
|
||||
`[
|
||||
["", "HH:mm:ss.SSS"],
|
||||
["PT1S", "HH:mm:ss"],
|
||||
["PT1M", "HH:mm"],
|
||||
["PT1H", "YYYY-MM-DD HH:mm"],
|
||||
["P1DT", "YYYY-MM-DD"],
|
||||
["P1YT", "YYYY"]
|
||||
]`,
|
||||
description: (
|
||||
'Values that define the format used in situations where timebased' +
|
||||
' data is rendered in order, and formatted timestamps should adapt to the' +
|
||||
' interval between measurements. Keys are' +
|
||||
' <a href="http://en.wikipedia.org/wiki/ISO_8601#Time_intervals" target="_blank">' +
|
||||
'ISO8601 intervals.</a>'
|
||||
)
|
||||
},
|
||||
'dateFormat:dow': {
|
||||
value: defaultWeekday,
|
||||
|
@ -113,14 +113,14 @@ export default function configDefaultsProvider() {
|
|||
attribution: 'Maps provided by USGS',
|
||||
styles: '',
|
||||
}
|
||||
}, null, ' '),
|
||||
}, null, 2),
|
||||
type: 'json',
|
||||
description: 'Default <a href="http://leafletjs.com/reference.html#tilelayer-wms" target="_blank">properties</a> for the WMS map server support in the tile map'
|
||||
},
|
||||
'visualization:colorMapping': {
|
||||
type: 'json',
|
||||
value: JSON.stringify({
|
||||
'Count': '#6eadc1'
|
||||
Count: '#6eadc1'
|
||||
}),
|
||||
description: 'Maps values to specified colors within visualizations'
|
||||
},
|
||||
|
@ -155,15 +155,14 @@ export default function configDefaultsProvider() {
|
|||
},
|
||||
'format:defaultTypeMap': {
|
||||
type: 'json',
|
||||
value: [
|
||||
'{',
|
||||
' "ip": { "id": "ip", "params": {} },',
|
||||
' "date": { "id": "date", "params": {} },',
|
||||
' "number": { "id": "number", "params": {} },',
|
||||
' "_source": { "id": "_source", "params": {} },',
|
||||
' "_default_": { "id": "string", "params": {} }',
|
||||
'}',
|
||||
].join('\n'),
|
||||
value:
|
||||
`{
|
||||
"ip": { "id": "ip", "params": {} },
|
||||
"date": { "id": "date", "params": {} },
|
||||
"number": { "id": "number", "params": {} },
|
||||
"_source": { "id": "_source", "params": {} },
|
||||
"_default_": { "id": "string", "params": {} }
|
||||
}`,
|
||||
description: 'Map of the format name to use by default for each field type. ' +
|
||||
'"_default_" is used if the field type is not mentioned explicitly'
|
||||
},
|
||||
|
@ -194,24 +193,22 @@ export default function configDefaultsProvider() {
|
|||
},
|
||||
'timepicker:timeDefaults': {
|
||||
type: 'json',
|
||||
value: [
|
||||
'{',
|
||||
' "from": "now-15m",',
|
||||
' "to": "now",',
|
||||
' "mode": "quick"',
|
||||
'}'
|
||||
].join('\n'),
|
||||
value:
|
||||
`{
|
||||
"from": "now-15m",
|
||||
"to": "now",
|
||||
"mode": "quick"
|
||||
}`,
|
||||
description: 'The timefilter selection to use when Kibana is started without one'
|
||||
},
|
||||
'timepicker:refreshIntervalDefaults': {
|
||||
type: 'json',
|
||||
value: [
|
||||
'{',
|
||||
' "display": "Off",',
|
||||
' "pause": false,',
|
||||
' "value": 0',
|
||||
'}'
|
||||
].join('\n'),
|
||||
value:
|
||||
`{
|
||||
"display": "Off",
|
||||
"pause": false,
|
||||
"value": 0
|
||||
}`,
|
||||
description: 'The timefilter\'s default refresh interval'
|
||||
},
|
||||
'dashboard:defaultDarkTheme': {
|
71
src/ui/settings/index.js
Normal file
71
src/ui/settings/index.js
Normal file
|
@ -0,0 +1,71 @@
|
|||
import { defaultsDeep } from 'lodash';
|
||||
import defaultsProvider from './defaults';
|
||||
|
||||
export default function setupSettings(kbnServer, server, config) {
|
||||
const uiSettings = {
|
||||
getAll,
|
||||
getDefaults,
|
||||
getUserProvided,
|
||||
set,
|
||||
setMany,
|
||||
remove
|
||||
};
|
||||
|
||||
server.decorate('server', 'uiSettings', () => uiSettings);
|
||||
|
||||
function getAll() {
|
||||
return Promise
|
||||
.all([getDefaults(), getUserProvided()])
|
||||
.then(([defaults, user]) => defaultsDeep(user, defaults));
|
||||
}
|
||||
|
||||
function getDefaults() {
|
||||
return Promise.resolve(defaultsProvider());
|
||||
}
|
||||
|
||||
function getUserProvided() {
|
||||
const { client } = server.plugins.elasticsearch;
|
||||
const clientSettings = getClientSettings(config);
|
||||
return client
|
||||
.get({ ...clientSettings })
|
||||
.then(res => res._source)
|
||||
.then(user => hydrateUserSettings(user));
|
||||
}
|
||||
|
||||
function setMany(changes) {
|
||||
const { client } = server.plugins.elasticsearch;
|
||||
const clientSettings = getClientSettings(config);
|
||||
return client
|
||||
.update({
|
||||
...clientSettings,
|
||||
body: { doc: changes }
|
||||
})
|
||||
.then(() => ({}));
|
||||
}
|
||||
|
||||
function set(key, value) {
|
||||
return setMany({ [key]: value });
|
||||
}
|
||||
|
||||
function remove(key) {
|
||||
return set(key, null);
|
||||
}
|
||||
}
|
||||
|
||||
function hydrateUserSettings(user) {
|
||||
return Object.keys(user).reduce(expand, {});
|
||||
function expand(expanded, key) {
|
||||
const userValue = user[key];
|
||||
if (userValue !== null) {
|
||||
expanded[key] = { userValue };
|
||||
}
|
||||
return expanded;
|
||||
}
|
||||
}
|
||||
|
||||
function getClientSettings(config) {
|
||||
const index = config.get('kibana.index');
|
||||
const id = config.get('pkg.version');
|
||||
const type = 'config';
|
||||
return { index, type, id };
|
||||
}
|
|
@ -26,4 +26,9 @@ export default class UiNavLinkCollection extends Collection {
|
|||
return this[inOrderCache];
|
||||
}
|
||||
|
||||
delete(value) {
|
||||
this[inOrderCache] = null;
|
||||
return super.delete(value);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ module.exports = function (grunt) {
|
|||
// TODO(sissel): Check if `fpm` is available
|
||||
if (!(/linux-x(86|64)$/.test(name))) return;
|
||||
|
||||
const arch = /x64$/.test(name) ? 'x86_64' : 'i686';
|
||||
const arch = /x64$/.test(name) ? 'x86_64' : 'i386';
|
||||
const fpm = args => exec('fpm', args);
|
||||
|
||||
const args = [
|
||||
|
|
|
@ -16,6 +16,7 @@ module.exports = function createServices(grunt) {
|
|||
'--install-prefix', service.outputDir,
|
||||
'--overwrite',
|
||||
'--user', 'kibana',
|
||||
'--group', 'kibana',
|
||||
'--sysv-log-path', '/var/log/kibana/',
|
||||
'-p', service.name,
|
||||
'-v', service.version,
|
||||
|
@ -25,6 +26,6 @@ module.exports = function createServices(grunt) {
|
|||
|
||||
grunt.file.mkdir(userScriptsDir);
|
||||
exec('please-manage-user', ['--output', userScriptsDir, 'kibana']);
|
||||
appendFileSync(resolve(userScriptsDir, 'installer.sh'), 'chown kibana /opt/kibana/optimize');
|
||||
appendFileSync(resolve(userScriptsDir, 'installer.sh'), 'chown kibana:kibana /opt/kibana/optimize');
|
||||
});
|
||||
};
|
||||
|
|
|
@ -10,8 +10,8 @@ module.exports = function (grunt) {
|
|||
|
||||
readdir(targetDir)
|
||||
.map(function (archive) {
|
||||
// only sha the archives
|
||||
if (!archive.match(/\.zip$|\.tar.gz$/)) return;
|
||||
// only sha the archives and packages
|
||||
if (!archive.match(/\.zip$|\.tar.gz$|\.deb$|\.rpm$/)) return;
|
||||
|
||||
return exec(cmd + archive + ' > ' + archive + '.sha1.txt', {
|
||||
cwd: targetDir
|
||||
|
|
20
tasks/config/packages.js
Normal file
20
tasks/config/packages.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
export default (grunt) => {
|
||||
const version = grunt.config.get('pkg.version');
|
||||
const productionPath = `kibana/${version.match(/\d\.\d/)[0]}`;
|
||||
const stagingPath = `kibana/staging/${version.match(/\d\.\d\.\d/)[0]}-XXXXXXX/repos/${version.match(/\d\./)[0]}x`;
|
||||
const rpmFolder = 'centos';
|
||||
const debFolder = 'debian';
|
||||
|
||||
return {
|
||||
staging: {
|
||||
bucket: 'download.elasticsearch.org',
|
||||
debPrefix: `${stagingPath}/${debFolder}`,
|
||||
rpmPrefix: `${stagingPath}/${rpmFolder}`
|
||||
},
|
||||
production: {
|
||||
bucket: 'packages.elasticsearch.org',
|
||||
debPrefix: `${productionPath}/${debFolder}`,
|
||||
rpmPrefix: `${productionPath}/${rpmFolder}`
|
||||
}
|
||||
};
|
||||
};
|
|
@ -26,12 +26,27 @@ module.exports = function (grunt) {
|
|||
let zipName = `${buildName}.zip`;
|
||||
let zipPath = resolve(rootPath, `target/${zipName}`);
|
||||
|
||||
let debName;
|
||||
let debPath;
|
||||
let rpmName;
|
||||
let rpmPath;
|
||||
if (name.match('linux')) {
|
||||
let debArch = name.match('x64') ? 'amd64' : 'i386';
|
||||
debName = `kibana_${version}_${debArch}.deb`;
|
||||
debPath = resolve(rootPath, `target/${debName}`);
|
||||
|
||||
let rpmArch = name.match('x64') ? 'x86_64' : 'i386';
|
||||
rpmName = `kibana-${version.replace('-', '_')}-1.${rpmArch}.rpm`;
|
||||
rpmPath = resolve(rootPath, `target/${rpmName}`);
|
||||
}
|
||||
return {
|
||||
name, win,
|
||||
nodeUrl, nodeDir,
|
||||
buildName, buildDir,
|
||||
tarName, tarPath,
|
||||
zipName, zipPath,
|
||||
debName, debPath,
|
||||
rpmName, rpmPath
|
||||
};
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
var _ = require('lodash');
|
||||
module.exports = function (grunt) {
|
||||
var { config } = grunt;
|
||||
|
||||
|
@ -6,22 +5,7 @@ module.exports = function (grunt) {
|
|||
release: {
|
||||
bucket: 'download.elasticsearch.org',
|
||||
access: 'private',
|
||||
debug: false,
|
||||
upload: config.get('platforms')
|
||||
.reduce(function (files, platform) {
|
||||
return files.concat(
|
||||
platform.tarName,
|
||||
platform.tarName + '.sha1.txt',
|
||||
platform.zipName,
|
||||
platform.zipName + '.sha1.txt'
|
||||
);
|
||||
}, [])
|
||||
.map(function (filename) {
|
||||
return {
|
||||
src: 'target/' + filename,
|
||||
dest: 'kibana/kibana/' + filename
|
||||
};
|
||||
})
|
||||
debug: false
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
36
tasks/rebuild/confirm.js
Normal file
36
tasks/rebuild/confirm.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { execFileSync } from 'child_process';
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { createInterface } from 'readline';
|
||||
|
||||
export default (grunt) => {
|
||||
grunt.registerTask('_rebuild:confirm', function () {
|
||||
const newVersion = grunt.option('buildversion') || grunt.config.get('pkg').version;
|
||||
const newBuildNum = grunt.option('buildnum') || grunt.config.get('buildNum');
|
||||
const newSha = grunt.option('buildsha') || grunt.config.get('buildSha');
|
||||
|
||||
grunt.config('rebuild', { newVersion, newBuildNum, newSha });
|
||||
|
||||
grunt.log.writeln('Targets will be rebuilt with the following:');
|
||||
grunt.log.writeln(`Version: ${newVersion}`);
|
||||
grunt.log.writeln(`Build number: ${newBuildNum}`);
|
||||
grunt.log.writeln(`Build sha: ${newSha}`);
|
||||
|
||||
const rl = createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
rl.on('close', this.async());
|
||||
|
||||
rl.question('Do you want to rebuild these packages? [N/y] ', (resp) => {
|
||||
const answer = resp.toLowerCase().trim();
|
||||
|
||||
if (answer === 'y') {
|
||||
grunt.config.set('rebuild.continue', true);
|
||||
}
|
||||
|
||||
rl.close();
|
||||
});
|
||||
});
|
||||
};
|
23
tasks/rebuild/create_archives.js
Normal file
23
tasks/rebuild/create_archives.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { execFileSync } from 'child_process';
|
||||
import { join } from 'path';
|
||||
|
||||
export default (grunt) => {
|
||||
grunt.registerTask('_rebuild:createArchives', function () {
|
||||
const buildDir = grunt.config.get('build');
|
||||
const targetDir = grunt.config.get('target');
|
||||
|
||||
grunt.file.mkdir('target');
|
||||
|
||||
grunt.file.expand({ cwd: buildDir }, '*').forEach(build => {
|
||||
const tar = join(targetDir, `${build}.tar.gz`);
|
||||
execFileSync('tar', ['-zchf', tar, build], { cwd: buildDir });
|
||||
|
||||
const zip = join(targetDir, `${build}.zip`);
|
||||
if (/windows/.test(build)) {
|
||||
execFileSync('zip', ['-rq', '-ll', zip, build], { cwd: buildDir });
|
||||
} else {
|
||||
execFileSync('zip', ['-rq', zip, build], { cwd: buildDir });
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
14
tasks/rebuild/extract_zips.js
Normal file
14
tasks/rebuild/extract_zips.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { execFileSync } from 'child_process';
|
||||
|
||||
export default (grunt) => {
|
||||
grunt.registerTask('_rebuild:extractZips', function () {
|
||||
const buildDir = grunt.config.get('build');
|
||||
const targetDir = grunt.config.get('target');
|
||||
|
||||
const zips = grunt.file.expand({ cwd: targetDir }, '*.zip');
|
||||
|
||||
zips.forEach(zip => {
|
||||
execFileSync('unzip', [zip, '-d', buildDir], { cwd: targetDir });
|
||||
});
|
||||
});
|
||||
};
|
66
tasks/rebuild/index.js
Normal file
66
tasks/rebuild/index.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
import { execSync } from 'child_process';
|
||||
import { trim } from 'lodash';
|
||||
|
||||
/**
|
||||
* Repackages all of the current archives in target/ with the same build
|
||||
* number, sha, and commit hash. This is useful when all you need to do is bump
|
||||
* the version of the release and do not want to introduce any other changes.
|
||||
*
|
||||
* Even if there are new commits, the standard build task reinstalls all npm
|
||||
* dependencies, which introduces at least a small amount of risk of
|
||||
* introducing bugs into the build since not all dependencies have fixed
|
||||
* versions.
|
||||
*
|
||||
* Options:
|
||||
* --skip-archives Will skip the archive step, useful for debugging
|
||||
* --buildversion="1.2.3" Sets new version to 1.2.3
|
||||
* --buildnum="99999" Sets new build number to 99999
|
||||
* --buildsha="9a5b2c1" Sets new build sha to 9a5b2c1 (use the full sha, though)
|
||||
*/
|
||||
export default (grunt) => {
|
||||
grunt.registerTask('rebuild', 'Rebuilds targets as a new version', function () {
|
||||
grunt.task.run([
|
||||
'_build:getProps',
|
||||
'_rebuild:confirm',
|
||||
'_rebuild:continue'
|
||||
]);
|
||||
});
|
||||
|
||||
grunt.registerTask('_rebuild:continue', function () {
|
||||
grunt.task.requires('_rebuild:confirm');
|
||||
|
||||
if (!grunt.config.get('rebuild.continue')) {
|
||||
grunt.log.writeln('Aborting without rebuilding anything');
|
||||
} else {
|
||||
grunt.task.run([
|
||||
'_rebuild:builds',
|
||||
'_rebuild:archives'
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask('_rebuild:builds', function () {
|
||||
grunt.task.requires('_rebuild:continue');
|
||||
|
||||
grunt.task.run([
|
||||
'clean:build',
|
||||
'_rebuild:extractZips',
|
||||
'_rebuild:updateBuilds'
|
||||
]);
|
||||
});
|
||||
|
||||
grunt.registerTask('_rebuild:archives', function () {
|
||||
grunt.task.requires('_rebuild:continue');
|
||||
|
||||
const skip = grunt.option('skip-archives');
|
||||
if (skip) {
|
||||
grunt.log.writeln('Skipping archive step since rebuild debugging was enabled');
|
||||
} else {
|
||||
grunt.task.run([
|
||||
'clean:target',
|
||||
'_rebuild:createArchives',
|
||||
'_build:shasums'
|
||||
]);
|
||||
}
|
||||
});
|
||||
};
|
57
tasks/rebuild/update_builds.js
Normal file
57
tasks/rebuild/update_builds.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
import { execFileSync } from 'child_process';
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
export default (grunt) => {
|
||||
grunt.registerTask('_rebuild:updateBuilds', function () {
|
||||
const buildDir = grunt.config.get('build');
|
||||
|
||||
const { newVersion, newBuildNum, newSha } = grunt.config.get('rebuild');
|
||||
|
||||
grunt.file.expand({ cwd: buildDir }, '*').forEach(build => {
|
||||
const thisBuildDir = join(buildDir, build);
|
||||
const thisBundlesDir = join(thisBuildDir, 'optimize', 'bundles');
|
||||
|
||||
const readmePath = join(thisBuildDir, 'README.txt');
|
||||
const pkgjsonPath = join(thisBuildDir, 'package.json');
|
||||
const bundlePaths = [
|
||||
...grunt.file.expand({ cwd: thisBundlesDir }, '*.bundle.js'),
|
||||
...grunt.file.expand({ cwd: thisBundlesDir }, '*.entry.js')
|
||||
];
|
||||
|
||||
const { oldBuildNum, oldSha, oldVersion } = readBuildInfo(pkgjsonPath);
|
||||
|
||||
replaceIn(readmePath, oldVersion, newVersion);
|
||||
replaceIn(pkgjsonPath, oldVersion, newVersion);
|
||||
replaceIn(pkgjsonPath, `"number": ${oldBuildNum},`, `"number": ${newBuildNum},`);
|
||||
replaceIn(pkgjsonPath, oldSha, newSha);
|
||||
bundlePaths
|
||||
.map(bundle => join(thisBundlesDir, bundle))
|
||||
.forEach(file => {
|
||||
replaceIn(file, `"kbnVersion":"${oldVersion}"`, `"kbnVersion":"${newVersion}"`);
|
||||
replaceIn(file, `"buildNum":${oldBuildNum}`, `"buildNum":${newBuildNum}`);
|
||||
});
|
||||
|
||||
const newBuild = build.replace(oldVersion, newVersion);
|
||||
if (build !== newBuild) {
|
||||
execFileSync('mv', [ build, newBuild ], { cwd: buildDir });
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function readBuildInfo(path) {
|
||||
const pkgjson = readFileSync(path).toString();
|
||||
const pkg = JSON.parse(pkgjson);
|
||||
return {
|
||||
oldBuildNum: pkg.build.number,
|
||||
oldSha: pkg.build.sha,
|
||||
oldVersion: pkg.version
|
||||
};
|
||||
}
|
||||
|
||||
function replaceIn(path, oldValue, newValue) {
|
||||
let contents = readFileSync(path).toString();
|
||||
contents = contents.replace(oldValue, newValue);
|
||||
writeFileSync(path, contents);
|
||||
}
|
|
@ -1,11 +1,15 @@
|
|||
module.exports = function (grunt) {
|
||||
var readline = require('readline');
|
||||
var url = require('url');
|
||||
var fs = require('fs');
|
||||
var _ = require('lodash');
|
||||
|
||||
// build, then zip and upload to s3
|
||||
grunt.registerTask('release', [
|
||||
'_release:confirmUpload',
|
||||
'_release:loadS3Config',
|
||||
'build',
|
||||
'_release:setS3Uploads',
|
||||
's3:release',
|
||||
'_release:complete'
|
||||
]);
|
||||
|
@ -33,18 +37,48 @@ module.exports = function (grunt) {
|
|||
});
|
||||
});
|
||||
|
||||
grunt.registerTask('_release:setS3Uploads', function () {
|
||||
var uploads = grunt.config.get('platforms')
|
||||
.reduce(function (files, platform) {
|
||||
return files.concat(
|
||||
platform.tarName,
|
||||
platform.tarName + '.sha1.txt',
|
||||
platform.zipName,
|
||||
platform.zipName + '.sha1.txt',
|
||||
platform.rpmName,
|
||||
platform.rpmName && platform.rpmName + '.sha1.txt',
|
||||
platform.debName,
|
||||
platform.debName && platform.debName + '.sha1.txt'
|
||||
);
|
||||
}, [])
|
||||
.filter(function (filename) {
|
||||
if (_.isUndefined(filename)) return false;
|
||||
try {
|
||||
fs.accessSync('target/' + filename, fs.F_OK);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.map(function (filename) {
|
||||
return {
|
||||
src: 'target/' + filename,
|
||||
dest: 'kibana/kibana/' + filename
|
||||
};
|
||||
});
|
||||
grunt.config.set('s3.release.upload', uploads);
|
||||
});
|
||||
|
||||
grunt.registerTask('_release:complete', function () {
|
||||
grunt.log.ok('Builds released');
|
||||
grunt.log.write(
|
||||
`
|
||||
${grunt.config.get('platforms').reduce((t, p) => {
|
||||
return (
|
||||
`${t}https://download.elastic.co/kibana/kibana/${p.buildName}.tar.gz
|
||||
https://download.elastic.co/kibana/kibana/${p.buildName}.zip
|
||||
`
|
||||
);
|
||||
}, '')}
|
||||
`
|
||||
);
|
||||
var links = grunt.config.get('s3.release.upload').reduce((t, {dest}) => {
|
||||
var link = url.format({
|
||||
protocol: 'https',
|
||||
hostname: 'download.elastic.co',
|
||||
pathname: dest
|
||||
});
|
||||
return `${t}${link}\n`;
|
||||
}, '');
|
||||
grunt.log.write(links);
|
||||
});
|
||||
};
|
||||
|
|
113
tasks/release_packages.js
Normal file
113
tasks/release_packages.js
Normal file
|
@ -0,0 +1,113 @@
|
|||
import exec from './utils/exec';
|
||||
import SimpleGit from 'simple-git';
|
||||
import { promisify } from 'bluebird';
|
||||
import readline from 'readline';
|
||||
|
||||
export default (grunt) => {
|
||||
const packages = grunt.config.get('packages');
|
||||
const platforms = grunt.config.get('platforms');
|
||||
|
||||
function debS3(deb) {
|
||||
exec('deb-s3', [
|
||||
'upload',
|
||||
'--preserve-versions',
|
||||
deb.filePath,
|
||||
'--bucket', deb.bucket,
|
||||
'--prefix', deb.prefix,
|
||||
'--sign', deb.signatureKeyId,
|
||||
'--arch', deb.arch,
|
||||
`--access-key-id=${deb.awsKey}`,
|
||||
`--secret-access-key=${deb.awsSecret}`
|
||||
]);
|
||||
}
|
||||
|
||||
function rpmS3(rpm) {
|
||||
exec('rpm', [
|
||||
'--resign', rpm.filePath,
|
||||
'--define', '_signature gpg',
|
||||
'--define', `_gpg_name ${rpm.signingKeyName}`
|
||||
]);
|
||||
|
||||
exec('rpm-s3', [
|
||||
'-v',
|
||||
'-b', rpm.bucket,
|
||||
'-p', rpm.prefix,
|
||||
'--sign',
|
||||
'--visibility', 'public-read',
|
||||
'-k', '100',
|
||||
rpm.filePath,
|
||||
'-r', 'external-1'
|
||||
], {
|
||||
env: Object.assign({}, {
|
||||
'AWS_ACCESS_KEY': rpm.awsKey,
|
||||
'AWS_SECRET_KEY': rpm.awsSecret
|
||||
}, process.env)
|
||||
});
|
||||
}
|
||||
|
||||
grunt.registerTask('publishPackages:staging', [
|
||||
'_publishPackages:confirm',
|
||||
'_publishPackages:upload:staging',
|
||||
]);
|
||||
|
||||
grunt.registerTask('publishPackages:production', [
|
||||
'_publishPackages:confirm',
|
||||
'_publishPackages:upload:production',
|
||||
]);
|
||||
|
||||
grunt.registerTask('_publishPackages:confirm', function () {
|
||||
function abort() {
|
||||
grunt.fail.fatal('Aborting publish');
|
||||
}
|
||||
|
||||
var rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
rl.on('close', this.async());
|
||||
rl.on('SIGINT', () => abort());
|
||||
rl.question('Publish packages to s3? [N/y] ', function (resp) {
|
||||
if (resp.toLowerCase().trim()[0] === 'y') return rl.close();
|
||||
abort();
|
||||
});
|
||||
});
|
||||
|
||||
grunt.registerTask('_publishPackages:upload', function (environment) {
|
||||
const aws = grunt.file.readJSON('.aws-config.json');
|
||||
const signature = grunt.file.readJSON('.signing-config.json');
|
||||
const done = this.async();
|
||||
const simpleGit = new SimpleGit();
|
||||
const revparse = promisify(simpleGit.revparse, simpleGit);
|
||||
|
||||
return revparse(['--short', 'HEAD'])
|
||||
.then(hash => {
|
||||
const trimmedHash = hash.trim();
|
||||
platforms.forEach((platform) => {
|
||||
if (platform.debPath) {
|
||||
debS3({
|
||||
filePath: platform.debPath,
|
||||
bucket: packages[environment].bucket,
|
||||
prefix: packages[environment].debPrefix.replace('XXXXXXX', trimmedHash),
|
||||
signatureKeyId: signature.id,
|
||||
arch: platform.name.match('x64') ? 'amd64' : 'i386',
|
||||
awsKey: aws.key,
|
||||
awsSecret: aws.secret
|
||||
});
|
||||
}
|
||||
|
||||
if (platform.rpmPath) {
|
||||
rpmS3({
|
||||
filePath: platform.rpmPath,
|
||||
bucket: packages[environment].bucket,
|
||||
prefix: packages[environment].rpmPrefix.replace('XXXXXXX', trimmedHash),
|
||||
signingKeyName: signature.name,
|
||||
awsKey: aws.key,
|
||||
awsSecret: aws.secret
|
||||
});
|
||||
}
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
};
|
|
@ -42,13 +42,9 @@ import {
|
|||
.catch(common.handleError(this));
|
||||
});
|
||||
|
||||
bdd.it('default request reponse should contain .kibana' , function () {
|
||||
bdd.it('default request response should contain .kibana' , function () {
|
||||
var expectedResponseContains = '"_index": ".kibana",';
|
||||
var elasticsearch = common.getEsHostPort();
|
||||
return consolePage.setServer(elasticsearch)
|
||||
.then(function () {
|
||||
return consolePage.clickPlay();
|
||||
})
|
||||
return consolePage.clickPlay()
|
||||
.then(function () {
|
||||
return common.try(function () {
|
||||
return consolePage.getResponse()
|
||||
|
@ -62,7 +58,6 @@ import {
|
|||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
}());
|
||||
}());
|
||||
|
|
|
@ -94,7 +94,7 @@ import {
|
|||
|
||||
bdd.it('should show the correct hit count', function () {
|
||||
var expectedHitCount = '14,004';
|
||||
return common.tryForTime(20 * 1000, function tryingForTime() {
|
||||
return common.try(function tryingForTime() {
|
||||
return discoverPage.getHitCount()
|
||||
.then(function compareData(hitCount) {
|
||||
expect(hitCount).to.be(expectedHitCount);
|
||||
|
@ -243,7 +243,7 @@ import {
|
|||
|
||||
|
||||
function verifyChartData(expectedBarChartData) {
|
||||
return common.tryForTime(20 * 1000, function tryingForTime() {
|
||||
return common.try(function tryingForTime() {
|
||||
return discoverPage.getBarChartData()
|
||||
.then(function compareData(paths) {
|
||||
// the largest bars are over 100 pixels high so this is less than 1% tolerance
|
||||
|
|
|
@ -51,7 +51,7 @@ import {
|
|||
var expectedHitCount = '445';
|
||||
return discoverPage.query('php')
|
||||
.then(function () {
|
||||
return common.tryForTime(20 * 1000, function tryingForTime() {
|
||||
return common.try(function tryingForTime() {
|
||||
return discoverPage.getHitCount()
|
||||
.then(function compareData(hitCount) {
|
||||
expect(hitCount).to.be(expectedHitCount);
|
||||
|
@ -76,7 +76,7 @@ import {
|
|||
var expectedHitCount = '11,156';
|
||||
return discoverPage.query('_type:apache')
|
||||
.then(function () {
|
||||
return common.tryForTime(20 * 1000, function tryingForTime() {
|
||||
return common.try(function tryingForTime() {
|
||||
return discoverPage.getHitCount()
|
||||
.then(function compareData(hitCount) {
|
||||
expect(hitCount).to.be(expectedHitCount);
|
||||
|
@ -229,7 +229,7 @@ import {
|
|||
return common.sleep(2000);
|
||||
})
|
||||
.then(function () {
|
||||
return common.tryForTime(20 * 1000, function tryingForTime() {
|
||||
return common.try(function tryingForTime() {
|
||||
return discoverPage.getDocTableIndex(1)
|
||||
.then(function (rowData) {
|
||||
expect(rowData).to.be(ExpectedDoc);
|
||||
|
|
|
@ -102,7 +102,7 @@ import { bdd, common, discoverPage, headerPage, settingsPage, scenarioManager }
|
|||
var re = new RegExp(baseUrl + '/goto/[0-9a-f]{32}$');
|
||||
return discoverPage.clickShortenUrl()
|
||||
.then(function () {
|
||||
return common.tryForTime(20 * 1000, function tryingForTime() {
|
||||
return common.try(function tryingForTime() {
|
||||
return discoverPage.getShortenedUrl()
|
||||
.then(function (actualUrl) {
|
||||
expect(actualUrl).to.match(re);
|
||||
|
|
|
@ -32,7 +32,7 @@ import {
|
|||
});
|
||||
|
||||
bdd.it('should have index pattern in url', function url() {
|
||||
return common.tryForTime(5000, function () {
|
||||
return common.try(function tryingForTime() {
|
||||
return remote.getCurrentUrl()
|
||||
.then(function (currentUrl) {
|
||||
expect(currentUrl).to.contain('logstash-*');
|
||||
|
@ -80,7 +80,7 @@ import {
|
|||
});
|
||||
|
||||
bdd.it('should return to the add data landing page', function returnToPage() {
|
||||
return common.tryForTime(5000, function () {
|
||||
return common.try(function tryingForTime() {
|
||||
return common.findTestSubject('addData');
|
||||
})
|
||||
.catch(common.handleError(this));
|
||||
|
@ -88,7 +88,7 @@ import {
|
|||
|
||||
bdd.it('should remove index pattern from url', function indexNotInUrl() {
|
||||
// give the url time to settle
|
||||
return common.tryForTime(5000, function () {
|
||||
return common.try(function tryingForTime() {
|
||||
return remote.getCurrentUrl()
|
||||
.then(function (currentUrl) {
|
||||
common.debug('currentUrl = ' + currentUrl);
|
||||
|
|
|
@ -123,7 +123,7 @@ import {
|
|||
683, 1361, 1415, 707, 177, 27, 32, 175, 707, 1408, 1355, 726, 201, 29
|
||||
];
|
||||
|
||||
return common.tryForTime(5000, function () {
|
||||
return common.try(function tryingForTime() {
|
||||
return visualizePage.getXAxisLabels()
|
||||
.then(function compareLabels(labels) {
|
||||
common.debug('X-Axis labels = ' + labels);
|
||||
|
|
|
@ -65,7 +65,7 @@ import {
|
|||
var expectedCount = ['14,004', 'Count'];
|
||||
|
||||
// initial metric of "Count" is selected by default
|
||||
return common.tryForTime(2000, function () {
|
||||
return common.try(function tryingForTime() {
|
||||
return visualizePage.getMetric()
|
||||
.then(function (metricValue) {
|
||||
expect(expectedCount).to.eql(metricValue.split('\n'));
|
||||
|
@ -88,7 +88,7 @@ import {
|
|||
return visualizePage.clickGo();
|
||||
})
|
||||
.then(function () {
|
||||
return common.tryForTime(2000, function () {
|
||||
return common.try(function tryingForTime() {
|
||||
return visualizePage.getMetric()
|
||||
.then(function (metricValue) {
|
||||
expect(avgMachineRam).to.eql(metricValue.split('\n'));
|
||||
|
@ -110,7 +110,7 @@ import {
|
|||
return visualizePage.clickGo();
|
||||
})
|
||||
.then(function () {
|
||||
return common.tryForTime(2000, function () {
|
||||
return common.try(function tryingForTime() {
|
||||
return visualizePage.getMetric()
|
||||
.then(function (metricValue) {
|
||||
expect(sumPhpMemory).to.eql(metricValue.split('\n'));
|
||||
|
@ -133,7 +133,7 @@ import {
|
|||
return visualizePage.clickGo();
|
||||
})
|
||||
.then(function () {
|
||||
return common.tryForTime(2000, function () {
|
||||
return common.try(function tryingForTime() {
|
||||
return visualizePage.getMetric()
|
||||
.then(function (metricValue) {
|
||||
// only comparing the text label!
|
||||
|
@ -156,7 +156,7 @@ import {
|
|||
return visualizePage.clickGo();
|
||||
})
|
||||
.then(function () {
|
||||
return common.tryForTime(2000, function () {
|
||||
return common.try(function tryingForTime() {
|
||||
return visualizePage.getMetric()
|
||||
.then(function (metricValue) {
|
||||
expect(minTimestamp).to.eql(metricValue.split('\n'));
|
||||
|
@ -178,7 +178,7 @@ import {
|
|||
return visualizePage.clickGo();
|
||||
})
|
||||
.then(function () {
|
||||
return common.tryForTime(2000, function () {
|
||||
return common.try(function tryingForTime() {
|
||||
return visualizePage.getMetric()
|
||||
.then(function (metricValue) {
|
||||
expect(maxRelatedContentArticleModifiedTime).to.eql(metricValue.split('\n'));
|
||||
|
@ -204,7 +204,7 @@ import {
|
|||
return visualizePage.clickGo();
|
||||
})
|
||||
.then(function () {
|
||||
return common.tryForTime(2000, function () {
|
||||
return common.try(function tryingForTime() {
|
||||
return visualizePage.getMetric()
|
||||
.then(function (metricValue) {
|
||||
expect(standardDeviationBytes).to.eql(metricValue.split('\n'));
|
||||
|
@ -226,7 +226,7 @@ import {
|
|||
return visualizePage.clickGo();
|
||||
})
|
||||
.then(function () {
|
||||
return common.tryForTime(2000, function () {
|
||||
return common.try(function tryingForTime() {
|
||||
return visualizePage.getMetric()
|
||||
.then(function (metricValue) {
|
||||
expect(uniqueCountClientip).to.eql(metricValue.split('\n'));
|
||||
|
@ -264,7 +264,7 @@ import {
|
|||
return visualizePage.clickGo();
|
||||
})
|
||||
.then(function () {
|
||||
return common.tryForTime(2000, function () {
|
||||
return common.try(function tryingForTime() {
|
||||
return visualizePage.getMetric()
|
||||
.then(function (metricValue) {
|
||||
expect(percentileMachineRam).to.eql(metricValue.split('\n'));
|
||||
|
@ -290,7 +290,7 @@ import {
|
|||
return visualizePage.clickGo();
|
||||
})
|
||||
.then(function () {
|
||||
return common.tryForTime(2000, function () {
|
||||
return common.try(function tryingForTime() {
|
||||
return visualizePage.getMetric()
|
||||
.then(function (metricValue) {
|
||||
expect(percentileRankBytes).to.eql(metricValue.split('\n'));
|
||||
|
|
|
@ -61,7 +61,7 @@ import {
|
|||
})
|
||||
.then(function () {
|
||||
common.debug('Click field geo.coordinates');
|
||||
return common.tryForTime(1000, function () {
|
||||
return common.try(function tryingForTime() {
|
||||
return visualizePage.selectField('geo.coordinates');
|
||||
});
|
||||
})
|
||||
|
|
|
@ -20,6 +20,7 @@ define(function (require) {
|
|||
excludeInstrumentation: /.*/,
|
||||
|
||||
defaultTimeout: 90000,
|
||||
defaultTryTimeout: 40000, // tryForTime could include multiple 'find timeouts'
|
||||
defaultFindTimeout: 10000 // this is how long we try to find elements on page
|
||||
}, serverConfig);
|
||||
});
|
||||
|
|
|
@ -14,6 +14,7 @@ exports.bdd = kbnInternVars.bdd;
|
|||
exports.intern = kbnInternVars.intern;
|
||||
exports.config = exports.intern.config;
|
||||
exports.defaultTimeout = exports.config.defaultTimeout;
|
||||
exports.defaultTryTimeout = exports.config.defaultTryTimeout;
|
||||
exports.defaultFindTimeout = exports.config.defaultFindTimeout;
|
||||
exports.scenarioManager = new ScenarioManager(url.format(exports.config.servers.elasticsearch));
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { common, config, defaultFindTimeout, remote, shieldPage } from '../';
|
||||
import { common, config, defaultTryTimeout, defaultFindTimeout, remote, shieldPage } from '../';
|
||||
|
||||
export default (function () {
|
||||
var Promise = require('bluebird');
|
||||
|
@ -225,7 +225,7 @@ export default (function () {
|
|||
},
|
||||
|
||||
try(block) {
|
||||
return this.tryForTime(defaultFindTimeout, block);
|
||||
return this.tryForTime(defaultTryTimeout, block);
|
||||
},
|
||||
|
||||
log(...args) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue