Merge branch 'master' into feature/sort-dimensions-dragging

This commit is contained in:
Nicolás Bevacqua 2016-05-10 16:29:44 +02:00
commit 7bf698acc9
143 changed files with 4909 additions and 1285 deletions

View file

@ -55,7 +55,7 @@ Please make sure you have signed the [Contributor License Agreement](http://www.
npm run elasticsearch
```
- Start the development server. _On Windows, you'll need you use Git Bash, Cygwin, or a similar shell that exposes the `sh` command._
- Start the development server. _On Windows, you'll need you use Git Bash, Cygwin, or a similar shell that exposes the `sh` command. And to successfully build you'll need Cygwin optional packages zip, tar, and shasum._
```sh
npm start

View file

@ -62,7 +62,8 @@ module.exports = function (grunt) {
'postcss-unique-selectors': '1.0.0',
'postcss-minify-selectors': '1.4.6',
'postcss-single-charset': '0.3.0',
'regenerator': '0.8.36'
'regenerator': '0.8.36',
'readable-stream': '2.1.0'
}
};

View file

@ -61,6 +61,10 @@
# must be a positive integer.
# elasticsearch.requestTimeout: 30000
# List of Kibana client-side headers to send to Elasticsearch. To send *no* client-side
# headers, set this value to [] (an empty list).
# elasticsearch.requestHeadersWhitelist: [ authorization ]
# Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable.
# elasticsearch.shardTimeout: 0

View file

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

View file

@ -29,7 +29,9 @@ to `false`.
wait for Elasticsearch to respond to pings.
`elasticsearch.requestTimeout:`:: *Default: 300000* Time in milliseconds to wait for responses from the back end or
Elasticsearch. This value must be a positive integer.
`elasticsearch.shardTimeout:`:: *Default: 0* Time in milliseconds for Elasticsearch to wait for responses from shards. Set
`elasticsearch.requestHeadersWhitelist:`:: *Default: `[ 'authorization' ]`* List of Kibana client-side headers to send to Elasticsearch.
To send *no* client-side headers, set this value to [] (an empty list).
`elasticsearch.shardTimeout:`:: *Default: 0* Time in milliseconds for Elasticsearch to wait for responses from shards. Set
to 0 to disable.
`elasticsearch.startupTimeout:`:: *Default: 5000* Time in milliseconds to wait for Elasticsearch at Kibana startup before
retrying.

View file

@ -376,6 +376,10 @@ deprecated[4.2, The names of several Kibana server properties changed in the 4.2
+
*default*: `500000`
`elasticsearch.requestHeadersWhitelist:` added[5.0]:: List of Kibana client-side headers to send to Elasticsearch. To send *no* client-side headers, set this value to [] (an empty list).
+
*default*: `[ 'authorization' ]`
`elasticsearch.shardTimeout` added[4.2]:: How long Elasticsearch should wait for responses from shards. Set to 0 to disable.
+
*alias*: `shard_timeout` deprecated[4.2]

View file

@ -67,6 +67,7 @@
},
"dependencies": {
"@bigfunger/decompress-zip": "0.2.0-stripfix2",
"@elastic/datemath": "2.2.0",
"@spalger/angular-bootstrap": "0.12.1",
"@spalger/filesaver": "1.1.2",
"@spalger/leaflet-draw": "0.2.3",
@ -189,6 +190,7 @@
"simple-git": "1.8.0",
"sinon": "1.17.2",
"source-map": "0.4.4",
"source-map-support": "0.4.0",
"supertest-as-promised": "2.0.2"
},
"engines": {

View file

@ -5,6 +5,7 @@ import glob from 'glob-all';
import rimraf from 'rimraf';
import mkdirp from 'mkdirp';
import Logger from '../../lib/logger';
import { UnsupportedProtocolError } from '../../lib/errors';
import { download, _downloadSingle } from '../download';
import { join } from 'path';
@ -74,12 +75,12 @@ describe('kibana cli', function () {
});
});
it('should throw an ENOTFOUND error for an invalid url', function () {
it('should throw an UnsupportedProtocolError for an invalid url', function () {
const sourceUrl = 'i am an invalid url';
return _downloadSingle(settings, logger, sourceUrl)
.then(shouldReject, function (err) {
expect(err.message).to.match(/ENOTFOUND/);
expect(err).to.be.an(UnsupportedProtocolError);
expectWorkingPathEmpty();
});
});

View file

@ -1,5 +1,6 @@
import downloadHttpFile from './downloaders/http';
import downloadLocalFile from './downloaders/file';
import { UnsupportedProtocolError } from '../lib/errors';
import { parse } from 'url';
export function _downloadSingle(settings, logger, sourceUrl) {
@ -8,8 +9,10 @@ export function _downloadSingle(settings, logger, sourceUrl) {
if (/^file/.test(urlInfo.protocol)) {
downloadPromise = downloadLocalFile(logger, decodeURI(urlInfo.path), settings.tempArchiveFile);
} else {
} else if (/^https?/.test(urlInfo.protocol)) {
downloadPromise = downloadHttpFile(logger, sourceUrl, settings.tempArchiveFile, settings.timeout);
} else {
downloadPromise = Promise.reject(new UnsupportedProtocolError());
}
return downloadPromise;
@ -29,7 +32,9 @@ export function download(settings, logger) {
return _downloadSingle(settings, logger, sourceUrl)
.catch((err) => {
if (err.message === 'ENOTFOUND') {
const isUnsupportedProtocol = err instanceof UnsupportedProtocolError;
const isDownloadResourceNotFound = err.message === 'ENOTFOUND';
if (isUnsupportedProtocol || isDownloadResourceNotFound) {
return tryNext();
}
throw (err);

View file

@ -87,8 +87,9 @@ async function mergePackageData(settings, packages) {
*/
async function extractArchive(settings) {
const filter = {
paths: [ settings.plugins[0].folder ]
paths: [ `kibana/${settings.plugins[0].folder}` ]
};
await extractFiles(settings.tempArchiveFile, settings.workingPath, 2, filter);
}

View file

@ -0,0 +1 @@
export class UnsupportedProtocolError extends Error {};

View file

@ -0,0 +1,54 @@
let _ = require("lodash");
let Api = require('./api');
let parts = [
require('./es_5_0/aliases'),
require('./es_5_0/aggregations'),
require('./es_5_0/cat'),
require('./es_5_0/cluster'),
require('./es_5_0/count'),
require('./es_5_0/document'),
require('./es_5_0/field_stats'),
require('./es_5_0/filter'),
require('./es_5_0/nodes'),
require('./es_5_0/globals'),
require('./es_5_0/indices'),
require('./es_5_0/mappings'),
require('./es_5_0/percolator'),
require('./es_5_0/query'),
require('./es_5_0/snapshot_restore'),
require('./es_5_0/search'),
require('./es_5_0/settings'),
require('./es_5_0/templates')
];
function ES_5_0() {
Api.call(this, "es_5_0");
_.each(parts, function (apiSection) {
apiSection(this);
}, this);
}
ES_5_0.prototype = _.create(Api.prototype, {'constructor': ES_5_0});
(function (cls) {
cls.addEndpointDescription = function (endpoint, description) {
if (description) {
var url_params_def = {};
_.each(description.patterns || [], function (p) {
if (p.indexOf("{indices}") >= 0) {
url_params_def["ignore_unavailable"] = "__flag__";
url_params_def["allow_no_indices"] = "__flag__";
url_params_def["expand_wildcards"] = ["open", "closed"];
}
});
if (url_params_def) {
description.url_params = description.url_params || {};
_.defaults(description.url_params, url_params_def);
}
}
Object.getPrototypeOf(cls).addEndpointDescription.call(this, endpoint, description);
};
})(ES_5_0.prototype);
module.exports = new ES_5_0();

View file

@ -0,0 +1,436 @@
var simple_metric = {
__template: {field: ""},
field: "{field}",
missing: 0,
script: {
// populated by a global rule
}
}, field_metric = {
__template: {field: ""},
field: "{field}"
}, gap_policy = {
__one_of: ["skip", "insert_zeros"]
}, simple_pipeline = {
__template: {
buckets_path: ""
},
buckets_path: "",
format: "",
gap_policy: gap_policy
};
var rules = {
"*": {
"aggs": {
__template: {
"NAME": {
"AGG_TYPE": {}
}
}
},
"min": simple_metric,
"max": simple_metric,
"avg": simple_metric,
"sum": simple_metric,
"stats": simple_metric,
"extended_stats": simple_metric,
"value_count": {
__template: {
"field": ""
},
"field": "{field}",
"script": {
// populated by a global rule
}
},
"global": {},
"filter": {},
"filters": {
__template: {
"filters": {
"NAME": {}
}
},
"filters": {
"*": {__scope_link: "GLOBAL.filter"}
},
"other_bucket": {__one_of: [true, false]},
"other_bucket_key": ""
},
"missing": field_metric,
"nested": {
__template: {
"path": ""
},
"path": ""
},
"reverse_nested": {
__template: {
"path": ""
},
"path": ""
},
"terms": {
__template: {
"field": "",
"size": 10
},
"field": "{field}",
"size": 10,
"shard_size": 10,
"order": {
__template: {
"_term": "asc"
},
"_term": {__one_of: ["asc", "desc"]},
"_count": {__one_of: ["asc", "desc"]},
"*": {__one_of: ["asc", "desc"]}
},
"min_doc_count": 10,
"script": {
// populated by a global rule
},
"include": ".*",
"exclude": ".*",
"execution_hint": {__one_of: ["map", "global_ordinals", "global_ordinals_hash", "global_ordinals_low_cardinality"]},
"show_term_doc_count_error": {__one_of: [true, false]},
"collect_mode": {__one_of: ["depth_first", "breadth_first"]},
"missing": ""
},
"significant_terms": {
__template: {
"field": ""
},
"field": "{field}",
"size": 10,
"shard_size": 10,
"shard_min_doc_count": 10,
"min_doc_count": 10,
"include": {__one_of: ["*", {pattern: "", flags: ""}]},
"exclude": {__one_of: ["*", {pattern: "", flags: ""}]},
"execution_hint": {__one_of: ["map", "global_ordinals", "global_ordinals_hash"]},
"background_filter": {
__scope_link: "GLOBAL.filter"
},
"mutual_information": {
"include_negatives": {__one_of: [true, false]}
},
"chi_square": {
"include_negatives": {__one_of: [true, false]},
"background_is_superset": {__one_of: [true, false]}
},
"percentage": {},
"gnd": {
"background_is_superset": {__one_of: [true, false]}
},
"script_heuristic": {
__template: {
"script": "_subset_freq/(_superset_freq - _subset_freq + 1)"
},
"script": {
// populated by a global rule
}
}
},
"range": {
__template: {
"field": "",
"ranges": [
{"from": 50, "to": 100},
]
},
"field": "{field}",
"ranges": [
{"to": 50, "from": 100, "key": ""}
],
"keyed": {__one_of: [true, false]},
"script": {
// populated by a global rule
}
},
"date_range": {
__template: {
"field": "",
"ranges": [
{"from": "now-10d/d", "to": "now"},
]
},
"field": "{field}",
"format": "MM-yyy",
"ranges": [
{"to": "", "from": "", "key": ""}
],
"keyed": {__one_of: [true, false]},
"script": {
// populated by a global rule
}
},
"ip_range": {
__template: {
"field": "",
"ranges": [
{"from": "10.0.0.5", "to": "10.0.0.10"},
]
},
"field": "{field}",
"format": "MM-yyy",
"ranges": [
{"to": "", "from": "", "key": "", "mask": "10.0.0.127/25"}
],
"keyed": {__one_of: [true, false]},
"script": {
// populated by a global rule
}
},
"histogram": {
__template: {
"field": "price",
"interval": 50
},
"field": "{field}",
"interval": 50,
"min_doc_count": 0,
"order": {
__template: {
"_key": "asc"
},
"_key": {__one_of: ["asc", "desc"]},
"_count": {__one_of: ["asc", "desc"]},
"*": {__one_of: ["asc", "desc"]}
},
"keyed": {__one_of: [true, false]},
"missing": 0
},
"date_histogram": {
__template: {
"field": "date",
"interval": "month"
},
"field": "{field}",
"interval": {__one_of: ["year", "quarter", "week", "day", "hour", "minute", "second"]},
"min_doc_count": 0,
"order": {
__template: {
"_key": "asc"
},
"_key": {__one_of: ["asc", "desc"]},
"_count": {__one_of: ["asc", "desc"]},
"*": {__one_of: ["asc", "desc"]}
},
"keyed": {__one_of: [true, false]},
"pre_zone": "-01:00",
"post_zone": "-01:00",
"pre_zone_adjust_large_interval": {__one_of: [true, false]},
"factor": 1000,
"pre_offset": "1d",
"post_offset": "1d",
"format": "yyyy-MM-dd",
"time_zone": "00:00",
"missing": ""
},
"geo_distance": {
__template: {
"field": "location",
"origin": {"lat": 52.3760, "lon": 4.894},
"ranges": [
{"from": 100, "to": 300},
]
},
"field": "{field}",
"origin": {"lat": 0.0, "lon": 0.0},
"unit": {__one_of: ["mi", "km", "in", "yd", "m", "cm", "mm"]},
"ranges": [
{"from": 50, "to": 100}
],
"distance_type": {__one_of: ["arc", "sloppy_arc", "plane"]}
},
"geohash_grid": {
__template: {
"field": "",
"precision": 3
},
"field": "{field}",
"precision": {__one_of: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]},
"size": 10,
"shard_size": 10
},
"percentiles": {
__template: {
"field": "",
"percents": [1.0, 5.0, 25.0, 50.0, 75.0, 95.0, 99.0]
},
"field": "{field}",
"percents": {
__template: [1.0, 5.0, 25.0, 50.0, 75.0, 95.0, 99.0],
// mark type as list
__any_of: []
},
"script": {
// populated by a global rule
},
"compression": 100,
"method": {__one_of: ["hdr", "tdigest"]},
missing: 0
},
"cardinality": {
__template: {
"field": ""
},
"precision_threshold": 100,
"rehash": true,
"script": {
// populated by a global rule
},
missing: ""
},
"scripted_metric": {
__template: {
"init_script": "",
"map_script": "",
"combine_script": "",
"reduce_script": ""
},
"init_script": {
__scope_link: "GLOBAL.script"
},
"map_script": {
__scope_link: "GLOBAL.script"
},
"combine_script": {
__scope_link: "GLOBAL.script"
},
"reduce_script": {
__scope_link: "GLOBAL.script"
},
"lang": "groovy",
"params": {},
"reduce_params": {}
},
"geo_bounds": {
__template: {
field: ""
},
field: "{field}",
wrap_longitude: {__one_of: [true, false]}
},
"top_hits": {
__template: {
size: 10
},
from: 0,
size: 10,
sort: {
__template: [],
__scope_link: "_search.sort"
},
highlight: {},
explain: {__one_of: [true, false]},
_source: {
__template: "",
__scope_link: "_search._source"
},
script_fields: {
__scope_link: "_search.script_fields"
},
fielddata_fields: ["{field}"],
version: {__one_of: [true, false]}
},
"percentile_ranks": {
__template: {
field: "",
values: [10, 15]
},
field: "{field}",
values: [],
"script": {
// populated by a global rule
},
"compression": 100,
"method": {__one_of: ["hdr", "tdigest"]},
missing: 0
},
"sampler": {
__template: {},
"field": "{field}",
"script": {
// populated by a global rule
},
"shard_size": 100,
"max_docs_per_value": 3,
"execution_hint": {__one_of: ["map", "global_ordinals", "bytes_hash"]}
},
"children": {
__template: {
"type": "",
},
"type": ""
},
"derivative": simple_pipeline,
"avg_bucket": simple_pipeline,
"max_bucket": simple_pipeline,
"min_bucket": simple_pipeline,
"sum_bucket": simple_pipeline,
"moving_avg": {
__template: {
buckets_path: ""
},
buckets_path: "",
format: "",
gap_policy: gap_policy,
"window": 5,
model: {__one_of: ["simple", "linear", "ewma", "holt", "holt_winters"]},
settings: {
type: {__one_of: ["add", "mult"]},
alpha: 0.5,
beta: 0.5,
gamma: 0.5,
period: 7
}
},
"cumulative_sum": {
__template: {
buckets_path: ""
},
buckets_path: "",
format: ""
},
"serial_diff": {
__template: {
buckets_path: "",
lag: 7
},
lag: 7,
gap_policy: gap_policy,
buckets_path: "",
format: ""
},
"bucket_script": {
__template: {
buckets_path: "",
script: {}
},
buckets_path: "",
format: "",
gap_policy: gap_policy,
script: {
// populated by a global rule
}
},
"bucket_selector": {
__template: {
buckets_path: "",
script: {}
},
buckets_path: "",
gap_policy: gap_policy,
script: {
// populated by a global rule
}
}
}
};
module.exports = function (api) {
api.addGlobalAutocompleteRules('aggregations', rules);
api.addGlobalAutocompleteRules('aggs', rules);
};

View file

@ -0,0 +1,71 @@
module.exports = function (api) {
api.addEndpointDescription('_post_aliases', {
methods: ['POST'],
patterns: [
"_aliases",
],
data_autocomplete_rules: {
'actions': {
__template: [
{'add': {'index': 'test1', 'alias': 'alias1'}}
],
__any_of: [
{
add: {
index: '{index}',
alias: '',
filter: {},
routing: '1',
search_routing: '1,2',
index_routing: '1'
},
remove: {
index: '',
alias: ''
}
}
]
}
}
});
api.addEndpointDescription('_get_aliases', {
methods: ['GET'],
patterns: [
"_aliases",
]
});
var aliasRules = {
filter: {},
routing: '1',
search_routing: '1,2',
index_routing: '1'
};
api.addEndpointDescription('_post_alias', {
methods: ["POST", "PUT"],
patterns: [
"{indices}/_alias/{name}"
],
data_autocomplete_rules: aliasRules
});
api.addEndpointDescription('_delete_alias', {
methods: ["DELETE"],
patterns: [
"{indices}/_alias/{name}"
]
});
api.addEndpointDescription('_get_alias', {
methods: ["GET"],
patterns: [
"_alias",
"{indices}/_alias",
"{indices}/_alias/{name}",
"_alias/{name}"
]
});
api.addGlobalAutocompleteRules('aliases', {
'*': aliasRules
});
};

View file

@ -0,0 +1,56 @@
let _ = require("lodash");
function addSimpleCat(endpoint, api, params, patterns) {
var url_params = {"help": "__flag__", "v": "__flag__", "bytes": ["b"]};
_.each(params || [], function (p) {
if (_.isString(p)) {
url_params[p] = "__flag__";
}
else {
var k = Object.keys(p)[0];
url_params[k] = p[k];
}
});
api.addEndpointDescription(endpoint, {
match: endpoint,
url_params: url_params,
patterns: patterns || [endpoint]
});
}
function addNodeattrsCat(api) {
api.addEndpointDescription('_cat/nodeattrs', {
methods: ['GET'],
patterns: [
"_cat/nodeattrs"
],
url_params: {
help: "__flag__",
v: "__flag__",
h: ["node", "name", "id", "nodeId", "pid", "p", "host", "h", "ip", "i", "port", "po", "attr", "attr.name", "value", "attr.value"]
}
});
}
module.exports = function (api) {
addSimpleCat('_cat/aliases', api);
addSimpleCat('_cat/allocation', api, null, ['_cat/allocation', '_cat/allocation/{nodes}']);
addSimpleCat('_cat/count', api);
addSimpleCat('_cat/health', api, [
{"ts": ["false", "true"]}
]);
addSimpleCat('_cat/indices', api, [
{h: []},
"pri",
],
['_cat/indices', '_cat/indices/{indices}']);
addSimpleCat('_cat/master', api);
addSimpleCat('_cat/nodes', api);
addSimpleCat('_cat/pending_tasks', api);
addSimpleCat('_cat/recovery', api);
addSimpleCat('_cat/thread_pool', api);
addSimpleCat('_cat/shards', api);
addSimpleCat('_cat/plugins', api);
addSimpleCat('_cat/segments', api);
addNodeattrsCat(api);
};

View file

@ -0,0 +1,142 @@
module.exports = function (api) {
api.addEndpointDescription('_cluster/nodes/stats');
api.addEndpointDescription('_cluster/state', {
patterns: [
"_cluster/state",
"_cluster/state/{metrics}",
"_cluster/state/{metrics}/{indices}"
],
url_components: {
"metrics": ["version", "master_node", "nodes", "routing_table", "routing_node", "metadata", "blocks"]
}
});
api.addEndpointDescription('_cluster/health', {
url_params: {
"local": "__flag__",
"level": ["indices", "shards"],
"master_timeout": "30s",
"timeout": "30s",
"wait_for_status": ["yellow", "green"],
"wait_for_relocating_shards": 0,
"wait_for_active_shards": 0,
"wait_for_nodes": 0
}
});
api.addEndpointDescription('_cluster/pending_tasks');
api.addEndpointDescription('get_cluster/settings', {
patterns: [
'_cluster/settings'
]
});
api.addEndpointDescription('put_cluster/settings', {
methods: ['PUT'],
patterns: [
'_cluster/settings'
],
data_autocomplete_rules: {
persistent: {
cluster: {
routing: {
'allocation.enable': {__one_of: ["all", "primaries", "new_primaries", "none"]},
'allocation.disk.threshold_enabled': {__one_of: [false, true]},
'allocation.disk.watermark.low': '85%',
'allocation.disk.watermark.high': '90%',
'allocation.disk.include_relocations': {__one_of: [true, false]},
'allocation.disk.reroute_interval': '60s',
'allocation.exclude': {
'_ip': "",
'_name': "",
'_host': "",
'_id': ""
},
'allocation.include': {
'_ip': "",
'_name': "",
'_host': "",
'_id': ""
},
'allocation.require': {
'_ip': "",
'_name': "",
'_host': "",
'_id': ""
},
'allocation.awareness.attributes': [],
'allocation.awareness.force': {
'*': {
'values': []
}
},
'allocation.allow_rebalance': {__one_of: ['always', 'indices_primaries_active', 'indices_all_active']},
'allocation.cluster_concurrent_rebalance': 2,
'allocation.node_initial_primaries_recoveries': 4,
'allocation.node_concurrent_recoveries': 2,
'allocation.same_shard.host': {__one_of: [false, true]}
}
},
indices: {
breaker: {
"total.limit": "70%",
"fielddata.limit": "60%",
"fielddata.overhead": 1.03,
"request.limit": "40%",
"request.overhead": 1.0
}
}
},
transient: {
__scope_link: '.persistent'
}
}
});
api.addEndpointDescription('_cluster/reroute', {
methods: ['POST'],
url_params: {
explain: "__flag__",
dry_run: "__flag__"
},
data_autocomplete_rules: {
commands: [
{
move: {
__template: {
index: "",
shard: 0,
from_node: "",
to_node: ""
},
index: "{index}",
shard: 0,
from_node: "{node}",
to_node: "{node}"
},
cancel: {
__template: {
index: "",
shard: 0,
node: ""
},
index: "{index}",
shard: 0,
node: "{node}",
allow_primary: {__one_of: [true, false]}
},
allocate: {
__template: {
index: "",
shard: 0,
node: ""
},
index: "{index}",
shard: 0,
node: "{node}",
allow_primary: {__one_of: [true, false]}
}
}
],
dry_run: {__one_of: [true, false]}
}
});
};

View file

@ -0,0 +1,22 @@
module.exports = function (api) {
api.addEndpointDescription('_count', {
methods: ['GET', 'POST'],
priority: 10, // collides with get doc by id
patterns: [
"{indices}/{types}/_count",
"{indices}/_count",
"_count"
],
url_params: {
preference: ["_primary", "_primary_first", "_local", "_only_node:xyz", "_prefer_node:xyz", "_shards:2,3"],
routing: "",
min_score: 1.0,
terminate_after: 10,
},
data_autocomplete_rules: {
query: {
// populated by a global rule
}
}
});
};

View file

@ -0,0 +1,233 @@
module.exports = function (api) {
api.addEndpointDescription('_get_doc', {
methods: ['GET'],
patterns: [
"{index}/{type}/{id}"
],
url_params: {
"version": 1,
"routing": "",
"parent": "",
"_source": "",
"_source_exclude": "",
"_source_include": ""
}
});
api.addEndpointDescription('_get_doc_source', {
methods: ['GET'],
patterns: [
"{index}/{type}/{id}/_source"
],
url_params: {
"version": 1,
"routing": "",
"parent": "",
"_source_exclude": "",
"_source_include": ""
}
});
api.addEndpointDescription('_delete_doc', {
methods: ['DELETE'],
patterns: [
"{index}/{type}/{id}"
],
url_params: {
"version": 1,
"version_type": ["external", "internal"],
"routing": "",
"parent": ""
}
});
api.addEndpointDescription('index_doc', {
methods: ['PUT', 'POST'],
patterns: [
"{index}/{type}/{id}"
],
url_params: {
"version": 1,
"version_type": ["external", "internal"],
"op_type": ["create"],
"routing": "",
"parent": "",
"timestamp": "",
"ttl": "5m",
"consistency": ["qurom", "one", "all"],
"refresh": "__flag__",
"timeout": "1m"
}
});
api.addEndpointDescription('create_doc', {
methods: ['PUT', 'POST'],
patterns: [
"{index}/{type}/{id}/_create"
],
url_params: {
"version": 1,
"version_type": ["external", "internal"],
"routing": "",
"parent": "",
"timestamp": "",
"ttl": "5m",
"consistency": ["qurom", "one", "all"],
"refresh": "__flag__",
"timeout": "1m"
}
});
api.addEndpointDescription('index_doc_no_id', {
methods: ['POST'],
patterns: [
"{index}/{type}"
],
url_params: {
"version": 1,
"version_type": ["external", "internal"],
"routing": "",
"parent": "",
"timestamp": "",
"ttl": "5m",
"consistency": ["qurom", "one", "all"],
"refresh": "__flag__",
"timeout": "1m"
}
});
api.addEndpointDescription('_update', {
methods: ['POST'],
patterns: [
"{index}/{type}/{id}/_update"
],
url_params: {
"version": 1,
"version_type": ["force", "internal"],
"routing": "",
"parent": "",
"timestamp": "",
"consistency": ["qurom", "one", "all"],
"refresh": "__flag__",
"timeout": "1m",
"retry_on_conflict": 3,
"fields": ""
},
data_autocomplete_rules: {
"script": {
// populated by a global rule
},
"doc": {},
"upsert": {},
"scripted_upsert": {__one_of: [true, false]}
}
});
api.addEndpointDescription('_put_script', {
methods: ['POST', 'PUT'],
patterns: [
"_scripts/{lang}/{id}",
"_scripts/{lang}/{id}/_create"
],
url_components: {
"lang": [
"groovy",
"expressions"
]
},
data_autocomplete_rules: {
"script": ""
}
});
api.addEndpointDescription('_termvectors', {
methods: ['GET', 'POST'],
patterns: [
"{index}/{type}/_termvectors"
],
priority: 10, // collision with get doc
url_params: {
"fields": "",
"offsets": "__flag__",
"payloads": "__flag__",
"positions": "__flag__",
"term_statistics": "__flag__",
"field_statistics": "__flag__",
"routing": "",
"version": 1,
"version_type": ["external", "external_gt", "external_gte", "force", "internal"],
"parent": "",
"preference": ""
},
data_autocomplete_rules: {
fields: [
"{field}"
],
offsets: {__one_of: [false, true]},
payloads: {__one_of: [false, true]},
positions: {__one_of: [false, true]},
term_statistics: {__one_of: [true, false]},
field_statistics: {__one_of: [false, true]},
per_field_analyzer: {
__template: {"FIELD": ""},
"{field}": ""
},
routing: "",
version: 1,
version_type: ["external", "external_gt", "external_gte", "force", "internal"],
doc: {},
filter: { // TODO: Exclude from global filter rules
"max_num_terms": 1,
"min_term_freq": 1,
"max_term_freq": 1,
"min_doc_freq": 1,
"max_doc_freq": 1,
"min_word_length": 1,
"max_word_length": 1
}
}
});
api.addEndpointDescription('_termvectors_id', {
methods: ['GET', 'POST'],
patterns: [
"{index}/{type}/{id}/_termvectors"
],
url_params: {
"fields": "",
"offsets": "__flag__",
"payloads": "__flag__",
"positions": "__flag__",
"term_statistics": "__flag__",
"field_statistics": "__flag__",
"routing": "",
"version": 1,
"version_type": ["external", "external_gt", "external_gte", "force", "internal"],
"parent": "",
"preference": "",
"dfs": "__flag__"
},
data_autocomplete_rules: {
fields: [
"{field}"
],
offsets: {__one_of: [false, true]},
payloads: {__one_of: [false, true]},
positions: {__one_of: [false, true]},
term_statistics: {__one_of: [true, false]},
field_statistics: {__one_of: [false, true]},
dfs: {__one_of: [true, false]},
per_field_analyzer: {
__template: {"FIELD": ""},
"{field}": ""
},
routing: "",
version: 1,
version_type: ["external", "external_gt", "external_gte", "force", "internal"],
filter: { // TODO: Exclude from global filter rules
"max_num_terms": 1,
"min_term_freq": 1,
"max_term_freq": 1,
"min_doc_freq": 1,
"max_doc_freq": 1,
"min_word_length": 1,
"max_word_length": 1
}
}
});
};

View file

@ -0,0 +1,47 @@
module.exports = function (api) {
api.addEndpointDescription('_field_stats', {
methods: ['GET', 'POST'],
patterns: [
"_field_stats",
"{indices}/_field_stats"
],
url_params: {
fields: [],
level: ["cluster", "indices"],
ignore_unavailable: ["true", "false"],
allow_no_indices: [false, true],
expand_wildcards: ["open", "closed", "none", "all"]
},
data_autocomplete_rules: {
fields: [
"{field}",
],
index_constraints: {
"{field}": {
min_value: {
gt: "MIN",
gte: "MAX",
lt: "MIN",
lte: "MAX"
},
max_value: {
gt: "MIN",
gte: "MAX",
lt: "MIN",
lte: "MAX"
}
},
__template: {
"FIELD": {
min_value: {
gt: "MIN"
},
max_value: {
lt: "MAX"
}
}
}
}
}
});
};

View file

@ -0,0 +1,336 @@
var filters = {};
filters.and = {
__template: {
filters: [
{}
]
},
filters: [
{
__scope_link: '.'
}
],
};
filters.bool = {
__scope_link: 'GLOBAL.query'
};
filters.exists = {
__template: {
'field': 'FIELD_NAME'
},
'field': '{field}'
};
filters.ids = {
__template: {
'values': ['ID']
},
'type': '{type}',
'values': ['']
};
filters.limit = {
__template: {
value: 100
},
value: 100
};
filters.type = {
__template: {
value: 'TYPE'
},
value: '{type}'
};
filters.geo_bounding_box = {
__template: {
'FIELD': {
'top_left': {
'lat': 40.73,
'lon': -74.1
},
'bottom_right': {
'lat': 40.717,
'lon': -73.99
}
}
},
'{field}': {
top_left: {
lat: 40.73,
lon: -74.1
},
bottom_right: {
lat: 40.73,
lon: -74.1
}
},
type: {
__one_of: ['memory', 'indexed']
},
};
filters.geo_distance = {
__template: {
distance: 100,
distance_unit: 'km',
'FIELD': {
lat: 40.73,
lon: -74.1
}
},
distance: 100,
distance_unit: {
__one_of: ['km', 'miles']
},
distance_type: {
__one_of: ['arc', 'plane']
},
optimize_bbox: {
__one_of: ['memory', 'indexed', 'none']
},
'{field}': {
lat: 40.73,
lon: -74.1
},
};
filters.geo_distance_range = {
__template: {
from: 100,
to: 200,
distance_unit: 'km',
'FIELD': {
lat: 40.73,
lon: -74.1
}
},
from: 100,
to: 200,
distance_unit: {
__one_of: ['km', 'miles']
},
distance_type: {
__one_of: ['arc', 'plane']
},
include_lower: {
__one_of: [true, false]
},
include_upper: {
__one_of: [true, false]
},
'{field}': {
lat: 40.73,
lon: -74.1
}
};
filters.geo_polygon = {
__template: {
'FIELD': {
'points': [
{
lat: 40.73,
lon: -74.1
},
{
lat: 40.83,
lon: -75.1
}
]
}
},
'{field}': {
points: [
{
lat: 40.73,
lon: -74.1
}
]
}
};
filters.geo_shape = {
__template: {
'FIELD': {
shape: {
type: 'envelope',
coordinates: [
[-45, 45],
[45, -45]
]
},
'relation': 'within'
}
},
'{field}': {
shape: {
type: '',
coordinates: []
},
indexed_shape: {
id: '',
index: '{index}',
type: '{type}',
shape_field_name: 'shape'
},
relation: {
__one_of: ['within', 'intersects', 'disjoint']
}
}
};
filters.has_child = {
__template: {
type: 'TYPE',
filter: {}
},
type: '{type}',
query: {},
filter: {},
_scope: '',
min_children: 1,
max_children: 10
};
filters.has_parent = {
__template: {
parent_type: 'TYPE',
filter: {}
},
parent_type: '{type}',
query: {},
filter: {},
_scope: ''
};
filters.m = filters.missing = {
__template: {
field: 'FIELD'
},
existence: {
__one_of: [true, false]
},
null_value: {
__one_of: [true, false]
},
field: '{field}'
};
filters.not = {
__template: {
filter: {}
},
filter: {}
};
filters.range = {
__template: {
'FIELD': {
gte: 10,
lte: 20
}
},
"{field}": {
gte: 1,
gt: 1,
lte: 20,
lt: 20,
time_zone: "+1:00",
"format": "dd/MM/yyyy||yyyy",
execution: {__one_of: ["index", "fielddata"]}
}
};
filters.or = {
__template: {
filters: [
{}
]
},
filters: [
{
__scope_link: '.'
}
]
};
filters.prefix = {
__template: {
'FIELD': 'VALUE'
},
'{field}': ''
};
filters.query = {
// global query
};
filters.script = {
__template: {
script: {}
},
script: {
// populated by a global rule
}
};
filters.term = {
__template: {
'FIELD': 'VALUE'
},
'{field}': ''
};
filters.terms = {
__template: {
'FIELD': ['VALUE1', 'VALUE2']
},
field: ['{field}'],
execution: {
__one_of: ['plain', 'bool', 'and', 'or', 'bool_nocache', 'and_nocache', 'or_nocache']
}
};
filters.nested = {
__template: {
path: 'path_to_nested_doc',
query: {}
},
query: {},
path: '',
_name: ''
};
module.exports = function (api) {
api.addGlobalAutocompleteRules('filter', filters);
};

View file

@ -0,0 +1,24 @@
module.exports = function (api) {
api.addGlobalAutocompleteRules('highlight', {
pre_tags: {},
post_tags: {},
tags_schema: {},
fields: {
'{field}': {
fragment_size: 20,
number_of_fragments: 3
}
}
});
api.addGlobalAutocompleteRules('script', {
__template: {
inline: "SCRIPT"
},
inline: "SCRIPT",
file: "FILE_SCRIPT_NAME",
id: "SCRIPT_ID",
lang: "",
params: {}
});
};

View file

@ -0,0 +1,208 @@
module.exports = function (api) {
api.addEndpointDescription('_refresh', {
methods: ['POST'],
patterns: [
"_refresh",
"{indices}/_refresh"
],
});
api.addEndpointDescription('_flush', {
methods: ['POST'],
patterns: [
"_flush",
"{indices}/_flush"
],
url_params: {
wait_if_ongoing: [true, false],
force: [true, false]
}
});
api.addEndpointDescription('_flush_synced', {
methods: ['POST'],
patterns: [
"_flush/synced",
"{indices}/_flush/synced"
]
});
api.addEndpointDescription('_stats', {
patterns: [
"_stats",
"_stats/{metrics}",
"{indices}/_stats",
"{indices}/_stats/{metrics}",
],
url_components: {
"metrics": [
"docs",
"store",
"indexing",
"search",
"get",
"merge",
"refresh",
"flush",
"warmer",
"filter_cache",
"percolate",
"segments",
"fielddata",
"completion",
"translog",
"query_cache",
"commit",
"_all"
]
},
url_params: {
"fields": [],
"types": [],
"completion_fields": [],
"fielddata_fields": [],
"level": ["cluster", "indices", "shards"]
}
});
api.addEndpointDescription('_segments', {
patterns: [
"{indices}/_segments",
"_segments"
]
});
api.addEndpointDescription('_recovery', {
patterns: [
"{indices}/_recovery",
"_recovery"
],
url_params: {
detailed: "__flag__",
active_only: "__flag__",
human: "__flag__"
}
});
api.addEndpointDescription('_analyze', {
methods: ['GET', 'POST'],
patterns: [
"{indices}/_analyze",
"_analyze"
],
url_params: {
"analyzer": "",
"char_filters": [],
"field": "",
"filters": [],
"text": "",
"tokenizer": ""
}
});
api.addEndpointDescription('_validate_query', {
methods: ['GET', 'POST'],
patterns: [
"{indices}/_validate/query",
"_validate/query"
],
url_params: {
explain: "__flag__",
rewrite: "__flag__"
},
data_autocomplete_rules: {
query: {
// populated by a global rule
}
}
});
api.addEndpointDescription('_shard_stores', {
methods: ['GET'],
patterns: [
"{indices}/_shard_stores",
"_shard_stores"
],
url_params: {
status: ["green", "yellow", "red", "all"]
}
});
api.addEndpointDescription('__create_index__', {
methods: ['PUT'],
patterns: [
"{index}"
],
data_autocomplete_rules: {
mappings: {
__scope_link: '_put_mapping'
},
settings: {
__scope_link: '_put_settings'
},
aliases: {
__template: {
"NAME": {}
}
}
}
});
api.addEndpointDescription('__delete_indices__', {
methods: ['DELETE'],
patterns: [
"{indices}"
]
});
api.addEndpointDescription('_get_index_settings', {
methods: ['GET',],
patterns: [
"{indices}/_settings",
],
url_params: {
flat_settings: "__flag__"
}
});
api.addEndpointDescription('_get_index', {
methods: ['GET',],
patterns: [
"{indices}",
"{indices}/{feature}"
],
url_components: {
"feature": [
"_mappings",
"_aliases"
]
}
});
api.addEndpointDescription('_cache/clear', {
patterns: [
"_cache/clear",
"{indices}/_cache/clear"
]
});
api.addEndpointDescription('_upgrade', {
methods: ["POST"],
patterns: [
"_upgrade",
"{indices}/_upgrade"
],
url_params: {
wait_for_completion: "__flag__"
}
});
api.addEndpointDescription('_upgrade_status', {
methods: ["GET"],
patterns: [
"_upgrade",
"{indices}/_upgrade"
]
});
};

View file

@ -0,0 +1,218 @@
let _ = require("lodash");
var BOOLEAN = {
__one_of: [true, false]
};
module.exports = function (api) {
api.addEndpointDescription('_get_mapping', {
methods: ['GET'],
priority: 10, // collides with get doc by id
patterns: [
"{indices}/_mapping",
"{indices}/_mapping/{types}",
"{indices}/{types}/_mapping",
"_mapping"
]
});
api.addEndpointDescription('_get_field_mapping', {
methods: ['GET'],
priority: 10, // collides with get doc by id
patterns: [
"{indices}/_mapping/field/{fields}",
"{indices}/_mapping/{type}/field/{fields}"
],
url_params: {
"include_defaults": "__flag__"
}
});
api.addEndpointDescription('_delete_mapping', {
methods: ['DELETE'],
priority: 10, // collides with get doc by id
patterns: [
"{indices}/_mapping",
"{indices}/_mapping/{types}",
"{indices}/{types}/_mapping",
"_mapping"
]
});
api.addEndpointDescription('_put_type_mapping', {
methods: ['PUT', 'POST'],
patterns: [
"{indices}/{type}/_mapping",
"{indices}/_mapping/{type}"
],
priority: 10, // collides with put doc by id
data_autocomplete_rules: {
__template: {
properties: {
'FIELD': {}
}
},
'_source': {
'enabled': BOOLEAN
},
'_all': {
'enabled': BOOLEAN
},
'_field_names': {
'index': BOOLEAN
},
'_routing': {
'required': BOOLEAN,
},
'_index': {
'enabled': BOOLEAN
},
'_parent': {
__template: {
'type': ''
},
'type': '{type}'
},
'_timestamp': {
'enabled': BOOLEAN,
'format': 'YYYY-MM-dd',
'default': ""
},
'dynamic_date_formats': ['yyyy-MM-dd'],
'date_detection': BOOLEAN,
'numeric_detection': BOOLEAN,
'properties': {
'*': {
type: {
__one_of: ['text', 'keyword', 'float', 'double', 'byte', 'short', 'integer', 'long', 'date', 'boolean',
'binary', 'object', 'nested', "geo_point", "geo_shape"
]
},
// strings
store: BOOLEAN,
index: BOOLEAN,
term_vector: {
__one_of: ['no', 'yes', 'with_offsets', 'with_positions', 'with_positions_offsets']
},
boost: 1.0,
null_value: '',
norms: BOOLEAN,
index_options: {
__one_of: ['docs', 'freqs', 'positions']
},
analyzer: 'standard',
search_analyzer: 'standard',
include_in_all: {
__one_of: [false, true]
},
ignore_above: 10,
position_increment_gap: 0,
// numeric
precision_step: 4,
ignore_malformed: BOOLEAN,
// geo_point
lat_lon: {
__one_of: [true, false]
},
geohash: {
__one_of: [true, false]
},
geohash_precision: '1m',
geohash_prefix: {
__one_of: [true, false]
},
validate: {
__one_of: [true, false]
},
validate_lat: {
__one_of: [true, false]
},
validate_lon: {
__one_of: [true, false]
},
normalize: {
__one_of: [true, false]
},
normalize_lat: {
__one_of: [true, false]
},
normalize_lon: {
__one_of: [true, false]
},
// geo_shape
tree: {
__one_of: ['geohash', 'quadtree']
},
precision: '5km',
tree_levels: 12,
distance_error_pct: 0.025,
orientation: 'ccw',
// dates
format: {
__one_of: _.flatten([_.map(['date', 'date_time', 'date_time_no_millis',
'ordinal_date', 'ordinal_date_time', 'ordinal_date_time_no_millis',
'time', 'time_no_millis', 't_time', 't_time_no_millis',
'week_date', 'week_date_time', 'week_date_time_no_millis'], function (s) {
return ['basic_' + s, 'strict_' + s];
}),
[
'date', 'date_hour', 'date_hour_minute', 'date_hour_minute_second', 'date_hour_minute_second_fraction',
'date_hour_minute_second_millis', 'date_optional_time', 'date_time', 'date_time_no_millis',
'hour', 'hour_minute', 'hour_minute_second', 'hour_minute_second_fraction', 'hour_minute_second_millis',
'ordinal_date', 'ordinal_date_time', 'ordinal_date_time_no_millis', 'time', 'time_no_millis',
't_time', 't_time_no_millis', 'week_date', 'week_date_time', 'weekDateTimeNoMillis', 'week_year',
'weekyearWeek', 'weekyearWeekDay', 'year', 'year_month', 'year_month_day', 'epoch_millis', 'epoch_second'
]])
},
fielddata: {
filter: {
regex: '',
frequency: {
min: 0.001,
max: 0.1,
min_segment_size: 500
}
}
},
similarity: {
__one_of: ['default', 'BM25']
},
// objects
properties: {
__scope_link: '_put_mapping.{type}.properties'
},
// multi_field
fields: {
'*': {
__scope_link: '_put_mapping.type.properties.field'
}
},
copy_to: {__one_of: ['{field}', ['{field}']]},
// nested
include_in_parent: BOOLEAN,
include_in_root: BOOLEAN
}
}
}
});
api.addEndpointDescription('_put_mapping', {
methods: ['PUT'],
patterns: [
"{indices}/_mapping"
],
data_autocomplete_rules: {
'{type}': {
__scope_link: '_put_type_mapping'
}
}
});
};

View file

@ -0,0 +1,76 @@
module.exports = function (api) {
api.addEndpointDescription('_nodes/hot_threads', {
methods: ['GET'],
patterns: [
"_nodes/hot_threads",
"_nodes/{nodes}/hot_threads"
]
});
api.addEndpointDescription('_nodes/info', {
patterns: [
"_nodes",
"_nodes/{metrics}",
"_nodes/{nodes}",
"_nodes/{nodes}/{metrics}",
"_nodes/{nodes}/info/{metrics}"
],
url_components: {
"metrics": [
"settings",
"os",
"process",
"jvm",
"thread_pool",
"network",
"transport",
"http",
"plugins",
"_all"
]
}
});
api.addEndpointDescription('_nodes/stats', {
patterns: [
"_nodes/stats",
"_nodes/stats/{metrics}",
"_nodes/stats/{metrics}/{index_metric}",
"_nodes/{nodes}/stats",
"_nodes/{nodes}/stats/{metrics}",
"_nodes/{nodes}/stats/{metrics}/{index_metric}"
],
url_components: {
"metrics": [
"os",
"jvm",
"thread_pool",
"network",
"fs",
"transport",
"http",
"indices",
"process",
"breaker",
"_all"
],
"index_metric": [
"store",
"indexing",
"get",
"search",
"merge",
"flush",
"refresh",
"filter_cache",
"fielddata",
"docs",
"warmer",
"percolate",
"completion",
"segments",
"translog",
"query_cache",
"_all"
]
}
});
};

View file

@ -0,0 +1,90 @@
module.exports = function (api) {
api.addEndpointDescription('_put_percolator', {
priority: 10, // to override doc
methods: ['PUT', 'POST'],
patterns: [
"{index}/.percolator/{id}"
],
url_params: {
"version": 1,
"version_type": ["external", "internal"],
"op_type": ["create"],
"routing": "",
"parent": "",
"timestamp": "",
"ttl": "5m",
"consistency": ["qurom", "one", "all"],
"refresh": "__flag__",
"timeout": "1m"
},
data_autocomplete_rules: {
query: {}
}
});
api.addEndpointDescription('_percolate', {
methods: ['GET', 'POST'],
priority: 10, // to override doc
patterns: [
"{indices}/{type}/_percolate"
],
url_params: {
preference: ["_primary", "_primary_first", "_local", "_only_node:xyz", "_prefer_node:xyz", "_shards:2,3"],
routing: "",
ignore_unavailable: ["true", "false"],
percolate_format: ["ids"]
},
data_autocomplete_rules: {
doc: {},
query: {},
filter: {},
size: 10,
track_scores: {__one_of: [true, false]},
sort: "_score",
aggs: {},
highlight: {}
}
});
api.addEndpointDescription('_percolate_id', {
methods: ['GET', 'POST'],
patterns: [
"{indices}/{type}/{id}/_percolate"
],
url_params: {
routing: "",
ignore_unavailable: ["true", "false"],
percolate_format: ["ids"],
percolate_index: "{index}",
percolate_type: "{type}",
percolate_routing: "",
percolate_preference: ["_primary", "_primary_first", "_local", "_only_node:xyz", "_prefer_node:xyz", "_shards:2,3"],
version: 1,
version_type: ["external", "internal"]
},
data_autocomplete_rules: {
query: {},
filter: {},
size: 10,
track_scores: {__one_of: [true, false]},
sort: "_score",
aggs: {},
highlight: {}
}
});
api.addEndpointDescription('_percolate_count', {
methods: ['GET', 'POST'],
patterns: [
"{indices}/{type}/_percolate/count"
],
url_params: {
preference: ["_primary", "_primary_first", "_local", "_only_node:xyz", "_prefer_node:xyz", "_shards:2,3"],
routing: "",
ignore_unavailable: ["true", "false"],
percolate_format: ["ids"]
},
data_autocomplete_rules: {
doc: {},
query: {},
filter: {}
}
});
};

View file

@ -0,0 +1,618 @@
let _ = require("lodash");
var SPAN_QUERIES = {
// TODO add one_of for objects
span_first: {
__scope_link: '.span_first'
},
span_near: {
__scope_link: '.span_near'
},
span_or: {
__scope_link: '.span_or'
},
span_not: {
__scope_link: '.span_not'
},
span_term: {
__scope_link: '.span_term'
},
span_containing: {
__scope_link: '.span_containing'
},
span_within: {
__scope_link: '.span_within'
}
};
var DECAY_FUNC_DESC = {
__template: {
"FIELD": {
"origin": "",
"scale": ""
}
},
"{field}": {
"origin": "",
"scale": "",
"offset": "",
"decay": 0.5
}
},
SCORING_FUNCS = {
"script_score": {
__template: {
"script": "_score * doc['f'].value"
},
"script": {
//populated by a global rule
}
},
"boost_factor": 2.0,
"random_score": {
"seed": 314159265359
},
"linear": DECAY_FUNC_DESC,
"exp": DECAY_FUNC_DESC,
"gauss": DECAY_FUNC_DESC,
"field_value_factor": {
__template: {
"field": ""
},
"field": "{field}",
"factor": 1.2,
"modifier": {
__one_of: ["none", "log", "log1p", "log2p", "ln", "ln1p", "ln2p", "square", "sqrt", "reciprocal"]
}
}
};
module.exports = function (api) {
api.addGlobalAutocompleteRules('query', {
match: {
__template: {
'FIELD': 'TEXT'
},
'{field}': {
'query': '',
'operator': {
__one_of: ['and', 'or']
},
'type': {
__one_of: ['phrase', 'phrase_prefix', 'boolean']
},
'max_expansions': 10,
'analyzer': '',
'fuzziness': 1.0,
'prefix_length': 1
}
},
match_phrase: {
__template: {
'FIELD': 'PHRASE'
},
'{field}': {
query: '',
analyzer: ''
}
},
match_phrase_prefix: {
__template: {
'FIELD': 'PREFIX'
},
'{field}': {
query: '',
analyzer: '',
max_expansions: 10,
prefix_length: 1,
fuzziness: 0.1
}
},
multi_match: {
__template: {
'query': '',
'fields': []
},
query: '',
fields: ['{field}'],
use_dis_max: {
__template: true,
__one_of: [true, false]
},
tie_breaker: 0.0,
type: {__one_of: ['best_fields', 'most_fields', 'cross_fields', 'phrase', 'phrase_prefix']}
},
bool: {
must: [
{
__scope_link: '.'
}
],
must_not: [
{
__scope_link: '.'
}
],
should: [
{
__scope_link: '.'
}
],
filter: {
__scope_link: 'GLOBAL.filter'
},
minimum_number_should_match: 1,
boost: 1.0
},
boosting: {
positive: {
__scope_link: '.'
},
negative: {
__scope_link: '.'
},
negative_boost: 0.2
},
ids: {
type: '',
values: []
},
constant_score: {
__template: {
filter: {},
boost: 1.2
},
query: {},
filter: {},
boost: 1.2
},
dis_max: {
__template: {
tie_breaker: 0.7,
boost: 1.2,
queries: []
},
tie_breaker: 0.7,
boost: 1.2,
queries: [
{
__scope_link: '.'
}
]
},
field: {
'{field}': {
query: '',
boost: 2.0,
enable_position_increments: {
__template: false,
__one_of: [true, false]
}
}
},
fuzzy: {
'{field}': {
'value': '',
'boost': 1.0,
'fuzziness': 0.5,
'prefix_length': 0
}
},
has_child: {
__template: {
type: 'TYPE',
query: {}
},
'type': '{type}',
'score_mode': {
__one_of: ['none', 'max', 'sum', 'avg']
},
'_scope': '',
'query': {},
'min_children': 1,
'max_children': 10
},
has_parent: {
__template: {
parent_type: 'TYPE',
query: {}
},
'parent_type': '{type}',
'score_mode': {
__one_of: ['none', 'score']
},
'_scope': '',
'query': {}
},
match_all: {},
more_like_this: {
__template: {
'fields': ['FIELD'],
'like_text': 'text like this one',
'min_term_freq': 1,
'max_query_terms': 12
},
fields: ['{field}'],
like_text: '',
percent_terms_to_match: 0.3,
min_term_freq: 2,
max_query_terms: 25,
stop_words: [''],
min_doc_freq: 5,
max_doc_freq: 100,
min_word_len: 0,
max_word_len: 0,
boost_terms: 1,
boost: 1.0,
analyzer: '',
docs: [{
_index: "{index}",
_type: "{type}",
_id: ""
}],
ids: [""]
},
mlt: {
__template: {
'fields': ['FIELD'],
'like_text': 'text like this one',
'min_term_freq': 1,
'max_query_terms': 12
},
__scope_link: ".more_like_this"
},
prefix: {
__template: {
'FIELD': {
'value': ''
}
},
'{field}': {
value: '',
boost: 1.0
}
},
query_string: {
__template: {
'default_field': 'FIELD',
'query': 'this AND that OR thus'
},
query: '',
default_field: '{field}',
fields: ['{field}'],
default_operator: {
__one_of: ['OR', 'AND']
},
analyzer: '',
allow_leading_wildcard: {
__one_of: [true, false]
},
lowercase_expanded_terms: {
__one_of: [true, false]
},
enable_position_increments: {
__one_of: [true, false]
},
fuzzy_max_expansions: 50,
fuzziness: 0.5,
fuzzy_prefix_length: 0,
phrase_slop: 0,
boost: 1.0,
analyze_wildcard: {
__one_of: [false, true]
},
auto_generate_phrase_queries: {
__one_of: [false, true]
},
minimum_should_match: '20%',
lenient: {
__one_of: [false, true]
},
use_dis_max: {
__one_of: [true, false]
},
tie_breaker: 0,
time_zone: "+1:00"
},
simple_query_string: {
__template: {
query: "",
fields: []
},
query: "",
fields: ["{field}"],
default_operator: {__one_of: ["OR", "AND"]},
analyzer: "",
flags: "OR|AND|PREFIX",
lowercase_expanded_terms: {__one_of: [true, false]},
locale: "ROOT",
lenient: {__one_of: [true, false]}
},
range: {
__template: {
'FIELD': {
gte: 10,
lte: 20
}
},
'{field}': {
__template: {
gte: 10,
lte: 20
},
gte: 10,
gt: 10,
lte: 20,
lt: 20,
time_zone: "+1:00",
boost: 1.0,
"format": "dd/MM/yyyy||yyyy"
}
},
span_first: {
__template: {
'match': {
'span_term': {
'FIELD': 'VALUE'
}
},
'end': 3
},
match: SPAN_QUERIES
},
span_near: {
__template: {
'clauses': [
{
span_term: {
'FIELD': {
'value': 'VALUE'
}
}
}
],
slop: 12,
in_order: false
},
clauses: [
SPAN_QUERIES
],
slop: 12,
in_order: {
__one_of: [false, true]
},
collect_payloads: {
__one_of: [false, true]
}
},
span_term: {
__template: {
'FIELD': {
'value': 'VALUE'
}
},
'{field}': {
value: '',
boost: 2.0
}
},
span_not: {
__template: {
include: {
span_term: {
'FIELD': {
'value': 'VALUE'
}
}
},
exclude: {
span_term: {
'FIELD': {
'value': 'VALUE'
}
}
}
},
include: SPAN_QUERIES,
exclude: SPAN_QUERIES
},
span_or: {
__template: {
clauses: [
{
span_term: {
'FIELD': {
'value': 'VALUE'
}
}
}
]
},
clauses: [
SPAN_QUERIES
]
},
span_containing: {
__template: {
little: {
span_term: {
'FIELD': {
'value': 'VALUE'
}
}
},
big: {
span_near: {
'clauses': [
{
span_term: {
'FIELD': {
'value': 'VALUE'
}
}
},
{
span_term: {
'FIELD': {
'value': 'VALUE'
}
}
}
],
"slop": 5,
"in_order": false
}
}
},
little: SPAN_QUERIES,
big: SPAN_QUERIES
},
span_within: {
__template: {
little: {
span_term: {
'FIELD': {
'value': 'VALUE'
}
}
},
big: {
span_near: {
'clauses': [
{
span_term: {
'FIELD': {
'value': 'VALUE'
}
}
},
{
span_term: {
'FIELD': {
'value': 'VALUE'
}
}
}
],
"slop": 5,
"in_order": false
}
}
},
little: SPAN_QUERIES,
big: SPAN_QUERIES
},
term: {
__template: {
'FIELD': {
value: 'VALUE'
}
},
'{field}': {
value: '',
boost: 2.0
}
},
terms: {
__template: {
'FIELD': ['VALUE1', 'VALUE2']
},
'{field}': ['']
},
wildcard: {
__template: {
'FIELD': {
value: 'VALUE'
}
},
'{field}': {
value: '',
boost: 2.0
}
},
nested: {
__template: {
path: 'path_to_nested_doc',
query: {}
},
path: '',
query: {},
score_mode: {
__one_of: ['avg', 'total', 'max', 'none']
}
},
custom_filters_score: {
__template: {
query: {},
filters: [
{
filter: {}
}
]
},
query: {},
filters: [
{
filter: {},
boost: 2.0,
script: {
//populated by a global rule
}
}
],
score_mode: {
__one_of: ['first', 'min', 'max', 'total', 'avg', 'multiply']
},
max_boost: 2.0,
params: {},
lang: ''
},
indices: {
__template: {
indices: ['INDEX1', 'INDEX2'],
query: {}
},
indices: ['{index}'],
query: {},
no_match_query: {
__scope_link: '.'
}
},
geo_shape: {
__template: {
location: {},
relation: 'within'
},
__scope_link: '.filter.geo_shape'
},
// js hint gets confused here
/* jshint -W015 */
function_score: _.defaults({
__template: {
query: {},
functions: [
{}
]
},
query: {},
functions: [
_.defaults(
{
filter: {},
weight: 1.0
},
SCORING_FUNCS
)
],
boost: 1.0,
boost_mode: {__one_of: ["multiply", "replace", "sum", "avg", "max", "min"]},
score_mode: {__one_of: ["multiply", "sum", "first", "avg", "max", "min"]},
max_boost: 10,
min_score: 1.0
},
SCORING_FUNCS
)
});
};

View file

@ -0,0 +1,255 @@
module.exports = function (api) {
api.addEndpointDescription('_search', {
methods: ['GET', 'POST'],
priority: 10, // collides with get doc by id
patterns: [
"{indices}/{types}/_search",
"{indices}/_search",
"_search"
],
url_params: {
q: "",
df: "",
analyzer: "",
default_operator: ["AND", "OR"],
explain: "__flag__",
_source: "",
_source_include: "",
_source_exclude: "",
fields: [],
sort: "",
track_scores: "__flag__",
timeout: 1,
from: 0,
size: 10,
search_type: ["dfs_query_then_fetch", "dfs_query_and_fetch", "query_then_fetch", "query_and_fetch"],
terminate_after: 10,
lowercase_expanded_terms: ["true", "false"],
analyze_wildcard: "__flag__",
preference: ["_primary", "_primary_first", "_local", "_only_node:xyz", "_prefer_node:xyz", "_shards:2,3"],
scroll: "5m",
scroll_id: "",
routing: "",
request_cache: ["true", "false"]
},
data_autocomplete_rules: {
query: {
// populated by a global rule
},
aggs: {
__template: {
"NAME": {
"AGG_TYPE": {}
}
}
},
post_filter: {
__scope_link: 'GLOBAL.filter'
},
size: {
__template: 20
},
from: 0,
sort: {
__template: [
{
'FIELD': {
'order': 'desc'
}
}
],
__any_of: [
{
'{field}': {
'order': {
__one_of: ['desc', 'asc']
},
missing: {
__one_of: ['_last', '_first']
},
mode: {
__one_of: ['min', 'max', 'avg', 'sum']
},
nested_path: "",
nested_filter: {
__scope_link: "GLOBAL.filter"
}
}
},
'{field}',
'_score',
{
'_geo_distance': {
__template: {
"FIELD": {
lat: 40,
lon: -70
},
order: "asc"
},
"{field}": {
__one_of: [
{
__template: {
lat: 40,
lon: -70
},
lat: 40,
lon: -70
},
[
{
__template: {
lat: 40,
lon: -70
},
lat: 40,
lon: -70
}
],
[""],
""
]
},
distance_type: {__one_of: ["sloppy_arc", "arc", "plane"]},
sort_mode: {__one_of: ["min", "max", "avg"]},
order: {__one_of: ["asc", "desc"]},
unit: "km"
}
}
]
},
fields: ['{field}'],
fielddata_fields: ["{field}"],
script_fields: {
__template: {
'FIELD': {
'script': {
// populated by a global rule
}
}
},
'*': {
__scope_link: 'GLOBAL.script'
}
},
partial_fields: {
__template: {
'NAME': {
include: []
}
},
'*': {
include: [],
exclude: []
}
},
highlight: {
// populated by a global rule
},
_source: {
__one_of: [
"{field}",
["{field}"],
{
"include": {
__one_of: [
"{field}",
["{field}"]
]
},
"exclude": {
__one_of: [
"{field}",
["{field}"]
]
}
}
]
},
explain: {
__one_of: [true, false]
},
stats: [''],
timeout: "1s",
version: {__one_of: [true, false]}
}
});
api.addEndpointDescription('_search_template', {
methods: ['GET'],
patterns: [
"{indices}/{types}/_search/template",
"{indices}/_search/template",
"_search/template"
],
data_autocomplete_rules: {
"template": {
__one_of: [
{__scope_link: "_search"},
{__scope_link: "GLOBAL.script"}
]
},
"params": {}
}
});
api.addEndpointDescription('_render_search_template', {
methods: ['GET'],
patterns: [
"_render/template"
],
data_autocomplete_rules: {
__one_of: [
{"inline": {__scope_link: "_search"}},
{__scope_link: "GLOBAL.script"}
],
"params": {}
}
});
api.addEndpointDescription('_render_search_template_with_id', {
methods: ['GET'],
patterns: [
"_render/template/{id}"
],
data_autocomplete_rules: {
"params": {}
}
});
api.addEndpointDescription('_get_delete_search_template', {
methods: ['GET', 'DELETE'],
patterns: [
"_search/template/{id}"
]
});
api.addEndpointDescription('_put_search_template', {
methods: ['PUT'],
patterns: [
"_search/template/{id}"
],
data_autocomplete_rules: {
"template": {
__scope_link: "_search"
}
}
});
api.addEndpointDescription('_search_shards', {
methods: ['GET'],
priority: 10, // collides with get doc by id
patterns: [
"{indices}/{types}/_search_shards",
"{indices}/_search_shards",
"_search_shards"
],
url_params: {
preference: ["_primary", "_primary_first", "_local", "_only_node:xyz", "_prefer_node:xyz", "_shards:2,3"],
routing: "",
local: "__flag__"
}
});
};

View file

@ -0,0 +1,86 @@
module.exports = function (api) {
api.addEndpointDescription('_get_settings', {
patterns: [
"{indices}/_settings",
"_settings"
],
url_params: {
flat_settings: "__flag__"
}
});
api.addEndpointDescription('_put_settings', {
methods: ['PUT'],
patterns: [
"{indices}/_settings",
"_settings"
],
data_autocomplete_rules: {
refresh_interval: '1s',
number_of_shards: 5,
number_of_replicas: 1,
'blocks.read_only': {
__one_of: [false, true]
},
'blocks.read': {
__one_of: [true, false]
},
'blocks.write': {
__one_of: [true, false]
},
'blocks.metadata': {
__one_of: [true, false]
},
term_index_interval: 32,
term_index_divisor: 1,
'translog.flush_threshold_ops': 5000,
'translog.flush_threshold_size': '200mb',
'translog.flush_threshold_period': '30m',
'translog.disable_flush': {
__one_of: [true, false]
},
'cache.filter.max_size': '2gb',
'cache.filter.expire': '2h',
'gateway.snapshot_interval': '10s',
routing: {
allocation: {
include: {
tag: ''
},
exclude: {
tag: ''
},
require: {
tag: ''
},
total_shards_per_node: -1
}
},
'recovery.initial_shards': {
__one_of: ['quorum', 'quorum-1', 'half', 'full', 'full-1']
},
'ttl.disable_purge': {
__one_of: [true, false]
},
analysis: {
analyzer: {},
tokenizer: {},
filter: {},
char_filter: {}
},
'cache.query.enable': {
__one_of: [true, false]
},
shadow_replicas: {
__one_of: [true, false]
},
shared_filesystem: {
__one_of: [true, false]
},
data_path: 'path',
codec: {
__one_of: ['default', 'best_compression', 'lucene_default']
}
}
});
};

View file

@ -0,0 +1,153 @@
module.exports = function (api) {
api.addEndpointDescription('restore_snapshot', {
methods: ['POST'],
patterns: [
'_snapshot/{id}/{id}/_restore'
],
url_params: {
wait_for_completion: "__flag__"
},
data_autocomplete_rules: {
indices: "*",
ignore_unavailable: {__one_of: [true, false]},
include_global_state: false,
rename_pattern: "index_(.+)",
rename_replacement: "restored_index_$1"
}
});
api.addEndpointDescription('single_snapshot', {
methods: ['GET', 'DELETE'],
patterns: [
'_snapshot/{id}/{id}'
]
});
api.addEndpointDescription('all_snapshots', {
methods: ['GET'],
patterns: [
'_snapshot/{id}/_all'
]
});
api.addEndpointDescription('put_snapshot', {
methods: ['PUT'],
patterns: [
'_snapshot/{id}/{id}'
],
url_params: {
wait_for_completion: "__flag__"
},
data_autocomplete_rules: {
indices: "*",
ignore_unavailable: {__one_of: [true, false]},
include_global_state: {__one_of: [true, false]},
partial: {__one_of: [true, false]}
}
});
api.addEndpointDescription('_snapshot_status', {
methods: ['GET'],
patterns: [
'_snapshot/_status',
'_snapshot/{id}/_status',
'_snapshot/{id}/{ids}/_status'
]
});
function getRepositoryType(context, editor) {
var iter = editor.iterForCurrentLoc();
// for now just iterate back to the first "type" key
var t = iter.getCurrentToken();
var type;
while (t && t.type.indexOf("url") < 0) {
if (t.type === 'variable' && t.value === '"type"') {
t = editor.parser.nextNonEmptyToken(iter);
if (!t || t.type !== "punctuation.colon") {
// weird place to be in, but safe choice..
break;
}
t = editor.parser.nextNonEmptyToken(iter);
if (t && t.type === "string") {
type = t.value.replace(/"/g, '');
}
break;
}
t = editor.parser.prevNonEmptyToken(iter);
}
return type;
}
api.addEndpointDescription('put_repository', {
methods: ['PUT'],
patterns: [
'_snapshot/{id}'
],
data_autocomplete_rules: {
__template: {"type": ""},
"type": {
__one_of: ["fs", "url", "s3", "hdfs"]
},
"settings": {
__one_of: [{
//fs
__condition: {
lines_regex: String.raw`type["']\s*:\s*["']fs`
},
__template: {
location: "path"
},
location: "path",
compress: {__one_of: [true, false]},
concurrent_streams: 5,
chunk_size: "10m",
max_restore_bytes_per_sec: "20mb",
max_snapshot_bytes_per_sec: "20mb"
},
{// url
__condition: {
lines_regex: String.raw`type["']\s*:\s*["']url`
},
__template: {
url: ""
},
url: "",
concurrent_streams: 5
},
{ //s3
__condition: {
lines_regex: String.raw`type["']\s*:\s*["']s3`
},
__template: {
bucket: ""
},
bucket: "",
region: "",
base_path: "",
concurrent_streams: 5,
chunk_size: "10m",
compress: {__one_of: [true, false]}
},
{// hdfs
__condition: {
lines_regex: String.raw`type["']\s*:\s*["']hdfs`
},
__template: {
path: ""
},
uri: "",
path: "some/path",
load_defaults: {__one_of: [true, false]},
conf_location: "cfg.xml",
concurrent_streams: 5,
compress: {__one_of: [true, false]},
chunk_size: "10m"
}
]
}
}
});
};

View file

@ -0,0 +1,27 @@
module.exports = function (api) {
api.addEndpointDescription('_delete_template', {
methods: ['DELETE'],
patterns: [
"_template/{id}",
]
});
api.addEndpointDescription('_get_template', {
methods: ['GET'],
patterns: [
"_template/{id}",
"_template",
]
});
api.addEndpointDescription('_put_template', {
methods: ['PUT'],
patterns: [
"_template/{id}",
],
data_autocomplete_rules: {
template: 'index*',
warmers: {__scope_link: '_warmer'},
mappings: {__scope_link: '_put_mapping'},
settings: {__scope_link: '_put_settings'}
}
});
};

View file

@ -239,21 +239,19 @@ function setActiveApi(api) {
}
es.addServerChangeListener(function () {
var version = es.getVersion(), api;
if (!version || version.length == 0) {
api = "es_1_0";
}
else if (version[0] === "1") {
api = "es_1_0";
}
else if (version[0] === "2") {
api = "es_2_0";
}
else if (version[0] === "3") {
api = "es_2_0"; // TODO: change :)
}
else {
api = "es_1_0";
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) {

View file

@ -5,6 +5,8 @@ import healthCheck from './lib/health_check';
import exposeClient from './lib/expose_client';
import createProxy, { createPath } from './lib/create_proxy';
const DEFAULT_REQUEST_HEADERS = [ 'authorization' ];
module.exports = function ({ Plugin }) {
return new Plugin({
require: ['kibana'],
@ -20,6 +22,7 @@ module.exports = function ({ Plugin }) {
password: string(),
shardTimeout: number().default(0),
requestTimeout: number().default(30000),
requestHeadersWhitelist: array().items().single().default(DEFAULT_REQUEST_HEADERS),
pingTimeout: number().default(ref('requestTimeout')),
startupTimeout: number().default(5000),
ssl: object({

View file

@ -0,0 +1,76 @@
import expect from 'expect.js';
import mapUri from '../map_uri';
import sinon from 'sinon';
describe('plugins/elasticsearch', function () {
describe('lib/map_uri', function () {
let request;
beforeEach(function () {
request = {
path: '/elasticsearch/some/path',
headers: {
cookie: 'some_cookie_string',
'accept-encoding': 'gzip, deflate',
origin: 'https://localhost:5601',
'content-type': 'application/json',
'x-my-custom-header': '42',
accept: 'application/json, text/plain, */*',
authorization: '2343d322eda344390fdw42'
}
};
});
it('only sends the whitelisted request headers', function () {
const get = sinon.stub()
.withArgs('elasticsearch.url').returns('http://foobar:9200')
.withArgs('elasticsearch.requestHeadersWhitelist').returns(['x-my-custom-HEADER', 'Authorization']);
const config = function () { return { get: get }; };
const server = {
config: config
};
mapUri(server)(request, function (err, upstreamUri, upstreamHeaders) {
expect(err).to.be(null);
expect(upstreamHeaders).to.have.property('authorization');
expect(upstreamHeaders).to.have.property('x-my-custom-header');
expect(Object.keys(upstreamHeaders).length).to.be(2);
});
});
it('sends no headers if whitelist is set to []', function () {
const get = sinon.stub()
.withArgs('elasticsearch.url').returns('http://foobar:9200')
.withArgs('elasticsearch.requestHeadersWhitelist').returns([]);
const config = function () { return { get: get }; };
const server = {
config: config
};
mapUri(server)(request, function (err, upstreamUri, upstreamHeaders) {
expect(err).to.be(null);
expect(Object.keys(upstreamHeaders).length).to.be(0);
});
});
it('sends no headers if whitelist is set to no value', function () {
const get = sinon.stub()
.withArgs('elasticsearch.url').returns('http://foobar:9200')
.withArgs('elasticsearch.requestHeadersWhitelist').returns([ null ]); // This is how Joi returns it
const config = function () { return { get: get }; };
const server = {
config: config
};
mapUri(server)(request, function (err, upstreamUri, upstreamHeaders) {
expect(err).to.be(null);
expect(Object.keys(upstreamHeaders).length).to.be(0);
});
});
});
});

View file

@ -3,12 +3,12 @@ import Promise from 'bluebird';
import Boom from 'boom';
import getBasicAuthRealm from './get_basic_auth_realm';
import toPath from 'lodash/internal/toPath';
import filterHeaders from './filter_headers';
module.exports = (client) => {
module.exports = (server, client) => {
return (req, endpoint, params = {}) => {
if (req.headers.authorization) {
_.set(params, 'headers.authorization', req.headers.authorization);
}
const filteredHeaders = filterHeaders(req.headers, server.config().get('elasticsearch.requestHeadersWhitelist'));
_.set(params, 'headers', filteredHeaders);
const path = toPath(endpoint);
const api = _.get(client, path);
let apiContext = _.get(client, path.slice(0, -1));

View file

@ -16,10 +16,12 @@ function createProxy(server, method, route, config) {
handler: {
proxy: {
mapUri: mapUri(server),
passThrough: true,
agent: createAgent(server),
xforward: true,
timeout: server.config().get('elasticsearch.requestTimeout')
timeout: server.config().get('elasticsearch.requestTimeout'),
onResponse: function (err, responseFromUpstream, request, reply) {
reply(err, responseFromUpstream);
}
}
},
};

View file

@ -78,8 +78,8 @@ module.exports = function (server) {
server.expose('ElasticsearchClientLogging', ElasticsearchClientLogging);
server.expose('client', client);
server.expose('createClient', createClient);
server.expose('callWithRequestFactory', callWithRequest);
server.expose('callWithRequest', callWithRequest(noAuthClient));
server.expose('callWithRequestFactory', _.partial(callWithRequest, server));
server.expose('callWithRequest', callWithRequest(server, noAuthClient));
server.expose('errors', elasticsearch.errors);
return client;

View file

@ -0,0 +1,22 @@
import _ from 'lodash';
export default function (originalHeaders, headersToKeep) {
const normalizeHeader = function (header) {
if (!header) {
return '';
}
header = header.toString();
return header.trim().toLowerCase();
};
// Normalize list of headers we want to allow in upstream request
const headersToKeepNormalized = headersToKeep.map(normalizeHeader);
// Normalize original headers in request
const originalHeadersNormalized = _.mapKeys(originalHeaders, function (headerValue, headerName) {
return normalizeHeader(headerName);
});
return _.pick(originalHeaders, headersToKeepNormalized);
}

View file

@ -1,6 +1,9 @@
import querystring from 'querystring';
import { resolve } from 'url';
import filterHeaders from './filter_headers';
module.exports = function mapUri(server, prefix) {
const config = server.config();
return function (request, done) {
const path = request.path.replace('/elasticsearch', '');
@ -11,6 +14,7 @@ module.exports = function mapUri(server, prefix) {
}
const query = querystring.stringify(request.query);
if (query) url += '?' + query;
done(null, url);
const filteredHeaders = filterHeaders(request.headers, server.config().get('elasticsearch.requestHeadersWhitelist'));
done(null, url, filteredHeaders);
};
};

View file

@ -106,6 +106,8 @@ describe('docViews', function () {
expect($scope.filter.calledOnce).to.be(true);
cell.find('.fa-search-minus').first().click();
expect($scope.filter.calledTwice).to.be(true);
cell.find('.fa-asterisk').first().click();
expect($scope.filter.calledThrice).to.be(true);
});
it('should NOT apply a filter when clicking non-filterable fields', function () {
@ -115,6 +117,8 @@ describe('docViews', function () {
expect($scope.filter.calledOnce).to.be(false);
cell.find('.fa-search-minus').first().click();
expect($scope.filter.calledTwice).to.be(false);
cell.find('.fa-asterisk').first().click();
expect($scope.filter.calledOnce).to.be(true);
});
});

View file

@ -27,6 +27,15 @@
tooltip-append-to-body="1"
class="fa fa-columns"></i>
</span>
<span ng-if="!indexPattern.metaFields.includes(field)">
<i ng-click="filter('_exists_', field, '+')"
tooltip="Filter for field present"
tooltip-append-to-body="1"
class="fa fa-asterisk"></i>
</span>
<span ng-if="indexPattern.metaFields.includes(field)" tooltip="Unable to filter for presence of meta fields">
<i class="fa fa-asterisk text-muted"></i>
</span>
</td>
<td>

View file

@ -83,14 +83,15 @@ app.directive('dashboardGrid', function ($compile, Notifier) {
});
$scope.$on('$destroy', function () {
safeLayout.cancel();
$window.off('resize', safeLayout);
if (!gridster) return;
gridster.$widgets.each(function (i, el) {
const panel = getPanelFor(el);
removePanel(panel);
// stop any animations
panel.$el.stop();
removePanel(panel, true);
// not that we will, but lets be safe
makePanelSerializeable(panel);
});
@ -125,9 +126,9 @@ app.directive('dashboardGrid', function ($compile, Notifier) {
}
// tell gridster to remove the panel, and cleanup our metadata
function removePanel(panel) {
function removePanel(panel, silent) {
// remove from grister 'silently' (don't reorganize after)
gridster.remove_widget(panel.$el);
gridster.remove_widget(panel.$el, silent);
// destroy the scope
panel.$scope.$destroy();

View file

@ -3,7 +3,7 @@ import angular from 'angular';
import moment from 'moment';
import getSort from 'ui/doc_table/lib/get_sort';
import rison from 'rison-node';
import dateMath from 'ui/utils/date_math';
import dateMath from '@elastic/datemath';
import 'ui/doc_table';
import 'ui/visualize';
import 'ui/notify';

View file

@ -3,7 +3,11 @@
<div class="header">
<h2 class="title">Edit Saved Objects</h2>
<button class="btn btn-default controls" ng-click="exportAll()"><i aria-hidden="true" class="fa fa-download"></i> Export Everything</button>
<button file-upload="importAll(fileContents)" class="btn btn-default controls" ng-click><i aria-hidden="true" class="fa fa-upload"></i> Import</button>
<file-upload on-read="importAll(fileContents)" upload-selector="button.upload">
<button class="btn btn-default controls upload" ng-click>
<i aria-hidden="true" class="fa fa-upload"></i> Import
</button>
</file-upload>
</div>
<p>
From here you can delete saved objects, such as saved searches. You can also edit the raw data of saved objects. Typically objects are only modified via their associated application, which is probably what you should use instead of this screen. Each tab is limited to 100 results. You can use the filter to find objects not in the default list.

View file

@ -1,13 +1,13 @@
import { saveAs } from '@spalger/filesaver';
import _ from 'lodash';
import { extend, find, flattenDeep, partialRight, pick, pluck, sortBy } from 'lodash';
import angular from 'angular';
import registry from 'plugins/kibana/settings/saved_object_registry';
import objectIndexHTML from 'plugins/kibana/settings/sections/objects/_objects.html';
import 'ui/directives/file_upload';
import uiRoutes from 'ui/routes';
import uiModules from 'ui/modules';
const MAX_SIZE = Math.pow(2, 31) - 1;
const MAX_SIZE = Math.pow(2, 31) - 1;
uiRoutes
.when('/settings/objects', {
@ -41,9 +41,9 @@ uiModules.get('apps/settings')
});
$q.all(services).then(function (data) {
$scope.services = _.sortBy(data, 'title');
$scope.services = sortBy(data, 'title');
let tab = $scope.services[0];
if ($state.tab) $scope.currentTab = tab = _.find($scope.services, {title: $state.tab});
if ($state.tab) $scope.currentTab = tab = find($scope.services, {title: $state.tab});
$scope.$watch('state.tab', function (tab) {
if (!tab) $scope.changeTab($scope.services[0]);
@ -83,23 +83,23 @@ uiModules.get('apps/settings')
};
$scope.bulkDelete = function () {
$scope.currentTab.service.delete(_.pluck($scope.selectedItems, 'id')).then(refreshData).then(function () {
$scope.currentTab.service.delete(pluck($scope.selectedItems, 'id')).then(refreshData).then(function () {
$scope.selectedItems.length = 0;
});
};
$scope.bulkExport = function () {
const objs = $scope.selectedItems.map(_.partialRight(_.extend, {type: $scope.currentTab.type}));
const objs = $scope.selectedItems.map(partialRight(extend, {type: $scope.currentTab.type}));
retrieveAndExportDocs(objs);
};
$scope.exportAll = () => {
Promise.map($scope.services, (service) =>
service.service.scanAll('').then((results) =>
results.hits.map((hit) => _.extend(hit, {type: service.type}))
)
).then((results) => retrieveAndExportDocs(_.flattenDeep(results)));
};
$scope.exportAll = () => Promise
.map($scope.services, service => service.service
.scanAll('')
.then(result => result.hits.map(hit => extend(hit, { type: service.type })))
)
.then(results => retrieveAndExportDocs(flattenDeep(results)))
.catch(error => notify.error(error));
function retrieveAndExportDocs(objs) {
if (!objs.length) return notify.error('No saved objects to export.');
@ -108,7 +108,7 @@ uiModules.get('apps/settings')
body: {docs: objs.map(transformToMget)}
})
.then(function (response) {
saveToFile(response.docs.map(_.partialRight(_.pick, '_id', '_type', '_source')));
saveToFile(response.docs.map(partialRight(pick, '_id', '_type', '_source')));
});
}
@ -131,7 +131,7 @@ uiModules.get('apps/settings')
}
return Promise.map(docs, function (doc) {
const service = _.find($scope.services, {type: doc._type}).service;
const service = find($scope.services, {type: doc._type}).service;
return service.get().then(function (obj) {
obj.id = doc._id;
return obj.applyESResp(doc).then(function () {

View file

@ -7,6 +7,7 @@ import tabifyPm from 'ui/agg_response/tabify/tabify';
import AggResponseTabifyTableGroupProvider from 'ui/agg_response/tabify/_table_group';
import VisProvider from 'ui/vis';
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
import StateManagementAppStateProvider from 'ui/state_management/app_state';
describe('Controller', function () {
let $rootScope;
@ -17,6 +18,7 @@ describe('Controller', function () {
let $el;
let Vis;
let fixtures;
let AppState;
beforeEach(ngMock.module('kibana', 'kibana/table_vis'));
beforeEach(ngMock.inject(function ($injector) {
@ -24,6 +26,7 @@ describe('Controller', function () {
$rootScope = $injector.get('$rootScope');
$compile = $injector.get('$compile');
fixtures = require('fixtures/fake_hierarchical_data');
AppState = Private(StateManagementAppStateProvider);
TableGroup = Private(AggResponseTabifyTableGroupProvider);
Vis = Private(VisProvider);
}));
@ -57,6 +60,7 @@ describe('Controller', function () {
vis.aggs.forEach(function (agg, i) { agg.id = 'agg_' + (i + 1); });
$rootScope.vis = vis;
$rootScope.uiState = new AppState({uiState: {}}).makeStateful('uiState');
$rootScope.newScope = function (scope) { $scope = scope; };
$el = $('<div>')
@ -103,6 +107,23 @@ describe('Controller', function () {
expect(!$scope.tableGroups).to.be.ok();
});
it('sets the sort on the scope when it is passed as a vis param', function () {
const sortObj = {
columnIndex: 1,
direction: 'asc'
};
initController(new OneRangeVis({sort: sortObj}));
// modify the data to not have any buckets
const resp = _.cloneDeep(fixtures.oneRangeBucket);
resp.aggregations.agg_2.buckets = {};
attachEsResponseToScope(resp);
expect($scope.sort.columnIndex).to.equal(sortObj.columnIndex);
expect($scope.sort.direction).to.equal(sortObj.direction);
});
it('sets #hasSomeRows properly if the table group is empty', function () {
initController(new OneRangeVis());

View file

@ -8,7 +8,8 @@
<kbn-agg-table-group
group="tableGroups"
export-title="vis.title"
per-page="vis.params.perPage">
per-page="vis.params.perPage"
sort="sort">
</kbn-agg-table-group>
</div>
</div>

View file

@ -38,7 +38,11 @@ function TableVisTypeProvider(Private) {
defaults: {
perPage: 10,
showPartialRows: false,
showMeticsAtAllLevels: false
showMeticsAtAllLevels: false,
sort: {
columnIndex: null,
direction: null
}
},
editor: '<table-vis-params></table-vis-params>'
},

View file

@ -1,5 +1,6 @@
import AggResponseTabifyTabifyProvider from 'ui/agg_response/tabify/tabify';
import uiModules from 'ui/modules';
import { assign } from 'lodash';
// get the kibana/table_vis module, and make sure that it requires the "kibana" module if it
// didn't already
@ -10,6 +11,14 @@ const module = uiModules.get('kibana/table_vis', ['kibana']);
module.controller('KbnTableVisController', function ($scope, Private) {
const tabifyAggResponse = Private(AggResponseTabifyTabifyProvider);
var uiStateSort = ($scope.uiState) ? $scope.uiState.get('vis.params.sort') : {};
assign($scope.vis.params.sort, uiStateSort);
$scope.sort = $scope.vis.params.sort;
$scope.$watchCollection('sort', function (newSort) {
$scope.uiState.set('vis.params.sort', newSort);
});
$scope.$watch('esResponse', function (resp, oldResp) {
let tableGroups = $scope.tableGroups = null;
let hasSomeRows = $scope.hasSomeRows = null;

View file

@ -19,21 +19,21 @@ export default (kibana) => {
let config = kibana.config;
const testGlobs = ['src/ui/public/**/*.js'];
const testingPluginId = config.get('tests_bundle.pluginId');
const testingPluginIds = config.get('tests_bundle.pluginId');
if (testingPluginId) {
const plugin = plugins.byId[testingPluginId];
if (!plugin) throw new Error('Invalid testingPluginId :: unknown plugin ' + testingPluginId);
if (testingPluginIds) {
testGlobs.push('!src/ui/public/**/__tests__/**/*');
testingPluginIds.split(',').forEach((pluginId) => {
const plugin = plugins.byId[pluginId];
if (!plugin) throw new Error('Invalid testingPluginId :: unknown plugin ' + pluginId);
// add the modules from all of this plugins apps
for (let app of plugin.apps) {
modules = union(modules, app.getModules());
}
// add the modules from all of this plugins apps
for (let app of plugin.apps) {
modules = union(modules, app.getModules());
}
testGlobs.push(
'!src/ui/public/**/__tests__/**/*',
`${plugin.publicDir}/**/__tests__/**/*.js`
);
testGlobs.push(`${plugin.publicDir}/**/__tests__/**/*.js`);
});
} else {
// add the modules from all of the apps

View file

@ -110,8 +110,12 @@ module.exports = async function (kbnServer, server, config) {
method: 'GET',
path: '/goto/{urlId}',
handler: async function (request, reply) {
const url = await shortUrlLookup.getUrl(request.params.urlId);
reply().redirect(config.get('server.basePath') + url);
try {
const url = await shortUrlLookup.getUrl(request.params.urlId);
reply().redirect(config.get('server.basePath') + url);
} catch (err) {
reply(err);
}
}
});
@ -119,8 +123,12 @@ module.exports = async function (kbnServer, server, config) {
method: 'POST',
path: '/shorten',
handler: async function (request, reply) {
const urlId = await shortUrlLookup.generateUrlId(request.payload.url);
reply(urlId);
try {
const urlId = await shortUrlLookup.generateUrlId(request.payload.url);
reply(urlId);
} catch (err) {
reply(err);
}
}
});

View file

@ -3,10 +3,11 @@ import crypto from 'crypto';
export default function (server) {
async function updateMetadata(urlId, urlDoc) {
const client = server.plugins.elasticsearch.client;
const kibanaIndex = server.config().get('kibana.index');
try {
await client.update({
index: '.kibana',
index: kibanaIndex,
type: 'url',
id: urlId,
body: {
@ -25,9 +26,10 @@ export default function (server) {
async function getUrlDoc(urlId) {
const urlDoc = await new Promise((resolve, reject) => {
const client = server.plugins.elasticsearch.client;
const kibanaIndex = server.config().get('kibana.index');
client.get({
index: '.kibana',
index: kibanaIndex,
type: 'url',
id: urlId
})
@ -45,9 +47,10 @@ export default function (server) {
async function createUrlDoc(url, urlId) {
const newUrlId = await new Promise((resolve, reject) => {
const client = server.plugins.elasticsearch.client;
const kibanaIndex = server.config().get('kibana.index');
client.index({
index: '.kibana',
index: kibanaIndex,
type: 'url',
id: urlId,
body: {
@ -79,7 +82,6 @@ export default function (server) {
return {
async generateUrlId(url) {
const urlId = createUrlId(url);
const urlDoc = await getUrlDoc(urlId);
if (urlDoc) return urlId;

View file

@ -47,5 +47,9 @@ module.exports = Promise.method(function (kbnServer, server, config) {
// resend SIGINT
process.kill(process.pid, 'SIGINT');
});
process.on('unhandledRejection', function (reason, promise) {
server.log(['warning'], `Detected an unhandled Promise rejection.\n${reason}`);
});
});
});

View file

@ -0,0 +1,77 @@
import {values} from 'lodash';
import expect from 'expect.js';
import sinon from 'auto-release-sinon';
import pluginInit from '../plugin_init';
describe('Plugin init', () => {
const getPluginCollection = (plugins) => ({
byId: plugins,
toArray: () => values(plugins)
});
it('should call preInit before init', async () => {
const plugins = {
foo: {
id: 'foo',
init: sinon.spy(),
preInit: sinon.spy(),
requiredIds: []
},
bar: {
id: 'bar',
init: sinon.spy(),
preInit: sinon.spy(),
requiredIds: []
},
baz: {
id: 'baz',
init: sinon.spy(),
preInit: sinon.spy(),
requiredIds: []
}
};
await pluginInit(getPluginCollection(plugins));
expect(plugins.foo.preInit.calledBefore(plugins.foo.init)).to.be.ok();
expect(plugins.foo.preInit.calledBefore(plugins.bar.init)).to.be.ok();
expect(plugins.foo.preInit.calledBefore(plugins.baz.init)).to.be.ok();
expect(plugins.bar.preInit.calledBefore(plugins.foo.init)).to.be.ok();
expect(plugins.bar.preInit.calledBefore(plugins.bar.init)).to.be.ok();
expect(plugins.bar.preInit.calledBefore(plugins.baz.init)).to.be.ok();
expect(plugins.baz.preInit.calledBefore(plugins.foo.init)).to.be.ok();
expect(plugins.baz.preInit.calledBefore(plugins.bar.init)).to.be.ok();
expect(plugins.baz.preInit.calledBefore(plugins.baz.init)).to.be.ok();
});
it('should call preInits in correct order based on requirements', async () => {
const plugins = {
foo: {
id: 'foo',
init: sinon.spy(),
preInit: sinon.spy(),
requiredIds: ['bar', 'baz']
},
bar: {
id: 'bar',
init: sinon.spy(),
preInit: sinon.spy(),
requiredIds: []
},
baz: {
id: 'baz',
init: sinon.spy(),
preInit: sinon.spy(),
requiredIds: ['bar']
}
};
await pluginInit(getPluginCollection(plugins));
expect(plugins.bar.preInit.firstCall.calledBefore(plugins.foo.init.firstCall)).to.be.ok();
expect(plugins.bar.preInit.firstCall.calledBefore(plugins.baz.init.firstCall)).to.be.ok();
expect(plugins.baz.preInit.firstCall.calledBefore(plugins.foo.init.firstCall)).to.be.ok();
});
});

View file

@ -1,4 +1,5 @@
import { includes, keys } from 'lodash';
import pluginInit from './plugin_init';
module.exports = async function (kbnServer, server, config) {
if (!config.get('plugins.initialize')) {
@ -17,30 +18,5 @@ module.exports = async function (kbnServer, server, config) {
});
let path = [];
const initialize = async function (id) {
let plugin = plugins.byId[id];
if (includes(path, id)) {
throw new Error(`circular dependencies found: "${path.concat(id).join(' -> ')}"`);
}
path.push(id);
for (let reqId of plugin.requiredIds) {
if (!plugins.byId[reqId]) {
throw new Error(`Unmet requirement "${reqId}" for plugin "${id}"`);
}
await initialize(reqId);
}
await plugin.init();
path.pop();
};
for (let {id} of plugins) {
await initialize(id);
}
await pluginInit(plugins);
};

View file

@ -59,9 +59,11 @@ module.exports = class Plugin {
this.uiExportsSpecs = opts.uiExports || {};
this.requiredIds = opts.require || [];
this.version = opts.version || pkg.version;
this.externalPreInit = opts.preInit || _.noop;
this.externalInit = opts.init || _.noop;
this.configPrefix = opts.configPrefix || this.id;
this.getConfigSchema = opts.config || _.noop;
this.preInit = _.once(this.preInit);
this.init = _.once(this.init);
this[extendInitFns] = [];
@ -100,6 +102,10 @@ module.exports = class Plugin {
}
}
async preInit() {
return await this.externalPreInit(this.kbnServer.server);
}
async init() {
let { id, version, kbnServer, configPrefix } = this;
let { config } = kbnServer;

View file

@ -0,0 +1,35 @@
import { includes } from 'lodash';
export default async (plugins) => {
let path = [];
const initialize = async function (id, fn) {
let plugin = plugins.byId[id];
if (includes(path, id)) {
throw new Error(`circular dependencies found: "${path.concat(id).join(' -> ')}"`);
}
path.push(id);
for (let reqId of plugin.requiredIds) {
if (!plugins.byId[reqId]) {
throw new Error(`Unmet requirement "${reqId}" for plugin "${id}"`);
}
await initialize(reqId, fn);
}
await plugin[fn]();
path.pop();
};
const collection = plugins.toArray();
for (let {id} of collection) {
await initialize(id, 'preInit');
}
for (let {id} of collection) {
await initialize(id, 'init');
}
};

View file

@ -36,6 +36,10 @@ describe('AggTableGroup Directive', function () {
it('renders a simple split response properly', function () {
let vis = new Vis(indexPattern, 'table');
$scope.group = tabifyAggResponse(vis, fixtures.metricOnly);
$scope.sort = {
columnIndex: null,
direction: null
};
let $el = $('<kbn-agg-table-group group="group"></kbn-agg-table-group>');
$compile($el)($scope);

View file

@ -2,7 +2,8 @@
ng-if="rows.length"
rows="rows"
columns="formattedColumns"
per-page="perPage">
per-page="perPage"
sort="sort">
<div class="agg-table-controls">
<small>Export:</small>&nbsp;&nbsp;

View file

@ -15,6 +15,7 @@ uiModules
scope: {
table: '=',
perPage: '=?',
sort: '=?',
exportTitle: '=?'
},
controllerAs: 'aggTable',
@ -26,7 +27,6 @@ uiModules
controller: function ($scope) {
let self = this;
self.sort = null;
self._saveAs = require('@spalger/filesaver').saveAs;
self.csv = {
separator: config.get('csv:separator'),

View file

@ -9,12 +9,13 @@
<tbody ng-repeat-end>
<tr>
<td>
<kbn-agg-table-group ng-if="table.tables" group="table" per-page="perPage"></kbn-agg-table-group>
<kbn-agg-table-group ng-if="table.tables" group="table" per-page="perPage" sort="sort"></kbn-agg-table-group>
<kbn-agg-table
ng-if="table.rows"
table="table"
export-title="exportTitle"
per-page="perPage">
per-page="perPage"
sort="sort">
</kbn-agg-table>
</td>
</tr>
@ -32,12 +33,13 @@
<tbody>
<tr>
<td ng-repeat="table in columns">
<kbn-agg-table-group ng-if="table.tables" group="table" per-page="perPage"></kbn-agg-table-group>
<kbn-agg-table-group ng-if="table.tables" group="table" per-page="perPage" sort="sort"></kbn-agg-table-group>
<kbn-agg-table
ng-if="table.rows"
table="table"
export-title="exportTitle"
per-page="perPage">
per-page="perPage"
sort="sort">
</kbn-agg-table>
</td>
</tr>

View file

@ -12,6 +12,7 @@ uiModules
scope: {
group: '=',
perPage: '=?',
sort: '=?',
exportTitle: '=?'
},
compile: function ($el) {

View file

@ -75,6 +75,9 @@ describe('AggConfig Filters', function () {
expect(fieldParams).to.have.property('lte');
expect(fieldParams.lte).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(filter).to.have.property('meta');

View file

@ -0,0 +1,61 @@
import expect from 'expect.js';
import ngMock from 'ng_mock';
import AggTypeMetricMedianProvider from 'ui/agg_types/metrics/median';
import VisProvider from 'ui/vis';
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
describe('AggTypeMetricMedianProvider class', function () {
let vis;
let indexPattern;
let aggTypeMetricMedian;
let aggDsl;
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private) {
const Vis = Private(VisProvider);
indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
aggTypeMetricMedian = Private(AggTypeMetricMedianProvider);
let vis = new Vis(indexPattern, {
'title': 'New Visualization',
'type': 'metric',
'params': {
'fontSize': 60,
'handleNoResults': true
},
'aggs': [
{
'id': '1',
'type': 'median',
'schema': 'metric',
'params': {
'field': 'bytes',
'percents': [
50
]
}
}
],
'listeners': {}
});
// Grab the aggConfig off the vis (we don't actually use the vis for
// anything else)
let aggConfig = vis.aggs[0];
aggDsl = aggConfig.toDsl();
}));
it('requests the percentiles aggregation in the Elasticsearch query DSL', function () {
expect(Object.keys(aggDsl)[0]).to.be('percentiles');
});
it ('asks Elasticsearch for the 50th percentile', function () {
expect(aggDsl.percentiles.percents).to.eql([50]);
});
it ('asks Elasticsearch for array-based values in the aggregation response', function () {
expect(aggDsl.percentiles.keyed).to.be(false);
});
});

View file

@ -0,0 +1,40 @@
import expect from 'expect.js';
import ngMock from 'ng_mock';
import AggTypeMetricPercentileRanksProvider from 'ui/agg_types/metrics/percentile_ranks';
import VisProvider from 'ui/vis';
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
describe('AggTypeMetricPercentileRanksProvider class', function () {
let Vis;
let indexPattern;
let aggTypeMetricPercentileRanks;
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private) {
Vis = Private(VisProvider);
indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
aggTypeMetricPercentileRanks = Private(AggTypeMetricPercentileRanksProvider);
}));
it('uses the custom label if it is set', function () {
const vis = new Vis(indexPattern, {});
// Grab the aggConfig off the vis (we don't actually use the vis for
// anything else)
const aggConfig = vis.aggs[0];
aggConfig.params.customLabel = 'my custom field label';
aggConfig.params.values = [ 5000, 10000 ];
aggConfig.params.field = {
displayName: 'bytes'
};
const responseAggs = aggTypeMetricPercentileRanks.getResponseAggs(aggConfig);
const percentileRankLabelFor5kBytes = responseAggs[0].makeLabel();
const percentileRankLabelFor10kBytes = responseAggs[1].makeLabel();
expect(percentileRankLabelFor5kBytes).to.be('Percentile rank 5,000 of "my custom field label"');
expect(percentileRankLabelFor10kBytes).to.be('Percentile rank 10,000 of "my custom field label"');
});
});

View file

@ -0,0 +1,38 @@
import expect from 'expect.js';
import ngMock from 'ng_mock';
import AggTypeMetricPercentilesProvider from 'ui/agg_types/metrics/percentiles';
import VisProvider from 'ui/vis';
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
describe('AggTypeMetricPercentilesProvider class', function () {
let Vis;
let indexPattern;
let aggTypeMetricPercentiles;
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private) {
Vis = Private(VisProvider);
indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
aggTypeMetricPercentiles = Private(AggTypeMetricPercentilesProvider);
}));
it('uses the custom label if it is set', function () {
const vis = new Vis(indexPattern, {});
// Grab the aggConfig off the vis (we don't actually use the vis for
// anything else)
const aggConfig = vis.aggs[0];
aggConfig.params.customLabel = 'prince';
aggConfig.params.percents = [ 95 ];
aggConfig.params.field = {
displayName: 'bytes'
};
const responseAggs = aggTypeMetricPercentiles.getResponseAggs(aggConfig);
const ninetyFifthPercentileLabel = responseAggs[0].makeLabel();
expect(ninetyFifthPercentileLabel).to.be('95th percentile of prince');
});
});

View file

@ -33,9 +33,29 @@ describe('AggTypeMetricStandardDeviationProvider class', function () {
let avgLabel = responseAggs[1].makeLabel();
let upperStdDevLabel = responseAggs[2].makeLabel();
expect(lowerStdDevLabel).to.be('Lower custom label of memory');
expect(lowerStdDevLabel).to.be('Lower custom label');
expect(avgLabel).to.be('Average of memory'); // not expected to use custom label
expect(upperStdDevLabel).to.be('Upper custom label of memory');
expect(upperStdDevLabel).to.be('Upper custom label');
});
it('uses the default labels if custom label is not set', function () {
let vis = new Vis(indexPattern, {});
// Grab the aggConfig off the vis (we don't actually use the vis for
// anything else)
let aggConfig = vis.aggs[0];
aggConfig.params.field = {
displayName: 'memory'
};
let responseAggs = aggTypeMetricStandardDeviation.getResponseAggs(aggConfig);
let lowerStdDevLabel = responseAggs[0].makeLabel();
let avgLabel = responseAggs[1].makeLabel();
let upperStdDevLabel = responseAggs[2].makeLabel();
expect(lowerStdDevLabel).to.be('Lower Standard Deviation of memory');
expect(avgLabel).to.be('Average of memory'); // not expected to use custom label
expect(upperStdDevLabel).to.be('Upper Standard Deviation of memory');
});
});

View file

@ -8,7 +8,8 @@ export default function createDateHistogramFilterProvider(Private) {
return buildRangeFilter(agg.params.field, {
gte: start.valueOf(),
lte: start.add(interval).subtract(1, 'ms').valueOf()
lte: start.add(interval).subtract(1, 'ms').valueOf(),
format: 'epoch_millis'
}, agg.vis.indexPattern);
};

View file

@ -2,7 +2,7 @@ import _ from 'lodash';
import AggTypesMetricsMetricAggTypeProvider from 'ui/agg_types/metrics/metric_agg_type';
import AggTypesMetricsGetResponseAggConfigClassProvider from 'ui/agg_types/metrics/get_response_agg_config_class';
import AggTypesMetricsPercentilesProvider from 'ui/agg_types/metrics/percentiles';
export default function AggTypeMetricMaxProvider(Private) {
export default function AggTypeMetricMedianProvider(Private) {
let MetricAggType = Private(AggTypesMetricsMetricAggTypeProvider);
let getResponseAggConfigClass = Private(AggTypesMetricsGetResponseAggConfigClassProvider);
let percentiles = Private(AggTypesMetricsPercentilesProvider);
@ -22,6 +22,11 @@ export default function AggTypeMetricMaxProvider(Private) {
{
name: 'percents',
default: [50]
},
{
write(agg, output) {
output.params.keyed = false;
}
}
],
getResponseAggs: percentiles.getResponseAggs,

View file

@ -17,8 +17,9 @@ export default function AggTypeMetricPercentileRanksProvider(Private) {
makeLabel: function () {
let field = this.field();
let format = (field && field.format) || fieldFormats.getDefaultInstance('number');
const label = this.params.customLabel || this.fieldDisplayName();
return 'Percentile rank ' + format.convert(this.key, 'text') + ' of "' + this.fieldDisplayName() + '"';
return 'Percentile rank ' + format.convert(this.key, 'text') + ' of "' + label + '"';
}
};

View file

@ -16,7 +16,8 @@ export default function AggTypeMetricPercentilesProvider(Private) {
let valueProps = {
makeLabel: function () {
return ordinalSuffix(this.key) + ' percentile of ' + this.fieldDisplayName();
const label = this.params.customLabel || this.fieldDisplayName();
return ordinalSuffix(this.key) + ' percentile of ' + label;
}
};

View file

@ -11,11 +11,12 @@ export default function AggTypeMetricStandardDeviationProvider(Private) {
return details.valProp;
},
makeLabel: function () {
let details = this.keyedDetails(this.params.customLabel)[this.key];
return details.title + ' of ' + this.fieldDisplayName();
const fieldDisplayName = this.fieldDisplayName();
const details = this.keyedDetails(this.params.customLabel, fieldDisplayName);
return _.get(details, [this.key, 'title']);
},
keyedDetails: function (customLabel) {
const label = customLabel ? customLabel : 'Standard Deviation';
keyedDetails: function (customLabel, fieldDisplayName) {
const label = customLabel ? customLabel : 'Standard Deviation of ' + fieldDisplayName;
return {
std_lower: {
valProp: ['std_deviation_bounds', 'lower'],
@ -23,7 +24,7 @@ export default function AggTypeMetricStandardDeviationProvider(Private) {
},
avg: {
valProp: 'avg',
title: 'Average'
title: 'Average of ' + fieldDisplayName
},
std_upper: {
valProp: ['std_deviation_bounds', 'upper'],

View file

@ -1,5 +1,11 @@
import _ from 'lodash';
import { format as formatUrl, parse as parseUrl } from 'url';
import modules from 'ui/modules';
import Notifier from 'ui/notify/notifier';
import { UrlOverflowServiceProvider } from '../../error_url_overflow';
const URL_LIMIT_WARN_WITHIN = 150;
module.exports = function (chrome, internals) {
@ -26,7 +32,7 @@ module.exports = function (chrome, internals) {
return a.href;
}()))
.config(chrome.$setupXsrfRequestInterceptor)
.run(($location) => {
.run(($location, $rootScope, Private) => {
chrome.getFirstPathSegment = () => {
return $location.path().split('/')[1];
};
@ -34,6 +40,29 @@ module.exports = function (chrome, internals) {
chrome.getBreadcrumbs = () => {
return $location.path().split('/').slice(1);
};
const notify = new Notifier();
const urlOverflow = Private(UrlOverflowServiceProvider);
const check = (event) => {
if ($location.path() === '/error/url-overflow') return;
try {
if (urlOverflow.check($location.absUrl()) <= URL_LIMIT_WARN_WITHIN) {
notify.warning(`
The URL has gotten big and may cause Kibana
to stop working. Please simplify the data on screen.
`);
}
} catch (e) {
const { host, path, search, protocol } = parseUrl(window.location.href);
// rewrite the entire url to force the browser to reload and
// discard any potentially unstable state from before
window.location.href = formatUrl({ host, path, search, protocol, hash: '#/error/url-overflow' });
}
};
$rootScope.$on('$routeUpdate', check);
$rootScope.$on('$routeChangeStart', check);
});
require('../directives')(chrome, internals);

View file

@ -2,8 +2,10 @@ import moment from 'moment-timezone';
import _ from 'lodash';
export default function configDefaultsProvider() {
// wrapped in provider so that a new instance is given to each app/test
const weekdays = moment.weekdays().slice();
const [defaultWeekday] = weekdays;
// wrapped in provider so that a new instance is given to each app/test
return {
'buildNum': {
readonly: true
@ -46,6 +48,12 @@ export default function configDefaultsProvider() {
' <a href="http://en.wikipedia.org/wiki/ISO_8601#Time_intervals" target="_blank">' +
'ISO8601 intervals.</a>'
},
'dateFormat:dow': {
value: defaultWeekday,
description: 'What day should weeks start on?',
type: 'select',
options: weekdays
},
'defaultIndex': {
value: null,
description: 'The index to access if no index is set',
@ -228,6 +236,6 @@ export default function configDefaultsProvider() {
value: 5000,
description: 'The time in milliseconds which an information notification ' +
'will be displayed on-screen for. Setting to Infinity will disable.'
}
},
};
};

View file

@ -1,32 +1,80 @@
import _ from 'lodash';
import $ from 'jquery';
import uiModules from 'ui/modules';
let module = uiModules.get('kibana');
module.directive('fileUpload', function ($parse) {
let html = '<span class="dropzone" ng-transclude></span>';
module.directive('fileUpload', function () {
return {
restrict: 'A',
restrict: 'E',
transclude: true,
scope: {
onRead: '&',
onLocate: '&',
uploadSelector: '@'
},
template: html,
link: function ($scope, $elem, attrs) {
let onUpload = $parse(attrs.fileUpload);
let $button = $elem.find($scope.uploadSelector);
let $dropzone = $elem.find('.dropzone');
let $fileInput = $('<input type="file" style="opacity: 0" id="testfile" />');
$elem.after($fileInput);
const handleFile = (file) => {
if (_.isUndefined(file)) return;
$fileInput.on('change', function (e) {
let reader = new FileReader();
reader.onload = function (e) {
$scope.$apply(function () {
onUpload($scope, {fileContents: e.target.result});
});
};
if ($scope.onRead) {
let reader = new FileReader();
reader.onload = function (e) {
$scope.$apply(function () {
$scope.onRead({fileContents: e.target.result});
});
};
reader.readAsText(file);
}
let target = e.srcElement || e.target;
if (target && target.files && target.files.length) reader.readAsText(target.files[0]);
if ($scope.onLocate) {
$scope.onLocate({ file });
}
};
$dropzone.on('dragover', function (e) {
e.preventDefault();
e.stopPropagation();
}
);
$dropzone.on('dragenter', function (e) {
e.preventDefault();
e.stopPropagation();
}
);
$dropzone.on('drop', function (e) {
e.stopPropagation();
e.preventDefault();
const file = _.get(e, 'originalEvent.dataTransfer.files[0]');
if (file) {
handleFile(file);
}
});
$elem.on('click', function (e) {
$fileInput.val(null);
$fileInput.trigger('click');
});
if ($button) {
const $fileInput = $('<input type="file" style="opacity: 0; position:absolute; right: -999999999px" id="testfile" />');
$elem.append($fileInput);
$fileInput.on('change', function (e) {
let target = e.srcElement || e.target;
if (_.get(target, 'files.length')) {
handleFile(target.files[0]);
}
});
$button.on('click', function (e) {
$fileInput.val(null);
$fileInput.trigger('click');
});
}
}
};
});

View file

@ -1,5 +1,5 @@
import _ from 'lodash';
import dateMath from 'ui/utils/date_math';
import dateMath from '@elastic/datemath';
import moment from 'moment';
import 'ui/timepicker/quick_ranges';
import 'ui/timepicker/time_units';

View file

@ -1,5 +1,5 @@
import _ from 'lodash';
import dateMath from 'ui/utils/date_math';
import dateMath from '@elastic/datemath';
import uiModules from 'ui/modules';
uiModules.get('kibana').directive('validateDateMath', function () {

View file

@ -1,5 +1,5 @@
<%
let attributes = '';
var attributes = '';
if (timefield) {
attributes='class="discover-table-timefield" width="1%"';
} else if (sourcefield) {

View file

@ -0,0 +1,18 @@
<div class="app-container error-url-overflow-app">
<h3>
<i aria-hidden="true" class="fa fa-warning text-danger"></i> Woah there!
</h3>
<p>
That's a big URL you have there. I have some unfortunate news: Your browser doesn't play nice with Kibana's bacon-double-cheese-burger-with-extra-fries sized URL. To keep you from running into problems Kibana limits URLs in your browser to {{controller.limit}} characters for your safety.
</p>
<h3>Ok, how do I fix this?</h3>
<p>This usually only happens with big, complex dashboards, so you have some options:</p>
<ol>
<li>Remove some stuff from your dashboard. This will reduce the length of the URL and keep IE in a good place.</li>
<li>Don't use IE. Every other supported browser we know of doesn't have this limit.</li>
</ol>
<br>
<br>
<p><small>Foot note: Party size candy bars are tiny. Party size sub sandwiches are massive. Really makes you think.</small></p>
</div>

View file

@ -0,0 +1,30 @@
import uiRoutes from 'ui/routes';
import uiModules from 'ui/modules';
import KbnUrlProvider from 'ui/url';
import './error_url_overflow.less';
import template from './error_url_overflow.html';
import { UrlOverflowServiceProvider } from './url_overflow_service';
export * from './url_overflow_service';
uiRoutes
.when('/error/url-overflow', {
template,
controllerAs: 'controller',
controller: class OverflowController {
constructor(Private, config, $scope) {
const kbnUrl = Private(KbnUrlProvider);
const urlOverflow = Private(UrlOverflowServiceProvider);
if (!urlOverflow.get()) {
kbnUrl.redirectPath('/');
return;
}
this.url = urlOverflow.get();
this.limit = urlOverflow.failLength();
$scope.$on('$destroy', () => urlOverflow.clear());
}
}
});

View file

@ -0,0 +1,7 @@
.error-url-overflow-app {
padding: 25px;
pre {
white-space: pre-wrap;
}
}

View file

@ -0,0 +1,65 @@
const URL_MAX_IE = 2000;
const URL_MAX_OTHERS = 25000;
const IE_REGEX = /(;MSIE |Edge\/\d)/;
export class UrlOverflowService {
constructor() {
const key = 'error/url-overflow/url';
const store = window.sessionStorage || {
getItem() {},
setItem() {},
removeItem() {},
};
// FIXME: Couldn't find a way to test for browser compatibility without
// complex redirect and cookie based "feature-detection" page, so going
// with user-agent detection for now.
this._ieLike = IE_REGEX.test(window.navigator.userAgent);
this._val = store.getItem(key);
this._sync = () => {
if (this._val == null) store.removeItem(key);
else store.setItem(key, this._val);
};
}
failLength() {
return this._ieLike ? URL_MAX_IE : URL_MAX_OTHERS;
}
set(v) {
this._val = v;
this._sync();
}
get() {
return this._val;
}
check(absUrl) {
if (!this.get()) {
const urlLength = absUrl.length;
const remaining = this.failLength() - urlLength;
if (remaining > 0) {
return remaining;
}
this.set(absUrl);
}
throw new Error(`
The URL has gotten too big and kibana can no longer
continue. Please refresh to return to your previous state.
`);
}
clear() {
this._val = undefined;
this._sync();
}
}
export function UrlOverflowServiceProvider() {
return new UrlOverflowService();
}

View file

@ -1,110 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="Layer_1"
data-name="Layer 1"
viewBox="0 0 141.37 51"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="kibana.svg">
<metadata
id="metadata31">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>Kibana-Full-Logo</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#515151"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:window-width="1796"
inkscape:window-height="1079"
id="namedview29"
showgrid="false"
inkscape:zoom="2.0725756"
inkscape:cx="70.684998"
inkscape:cy="25.5"
inkscape:window-x="349"
inkscape:window-y="210"
inkscape:window-maximized="0"
inkscape:current-layer="Layer_1" />
<defs
id="defs3">
<style
id="style5">.cls-1,.cls-2,.cls-3,.cls-4{fill:#fff;}.cls-1{opacity:0.6;}.cls-2{opacity:0.4;}.cls-3{opacity:0.9;}</style>
</defs>
<title
id="title7">Kibana-Full-Logo</title>
<path
class="cls-1"
d="M409.68,273.59a38.94,38.94,0,0,1,18.71,4.76L448.2,254.5H408.94v19.11Z"
transform="translate(-408.94 -254.5)"
id="path9" />
<path
class="cls-1"
d="M428.39,278.35l-19.45,23.42v3.73h39.2A39.2,39.2,0,0,0,428.39,278.35Z"
transform="translate(-408.94 -254.5)"
id="path11" />
<path
class="cls-2"
d="M428.39,278.35l-19.45,23.42v3.73h7l18.87-22.77s-1.25-1.06-3-2.3C430.41,279.46,428.39,278.35,428.39,278.35Z"
transform="translate(-408.94 -254.5)"
id="path13" />
<path
class="cls-3"
d="M409.68,273.59l-0.74,0v28.17l19.45-23.42A38.94,38.94,0,0,0,409.68,273.59Z"
transform="translate(-408.94 -254.5)"
id="path15" />
<g
style="font-style:normal;font-weight:normal;font-size:33.04906845px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:0.92528737;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="text4159"
transform="translate(0,-13.509761)">
<path
d="m 53.346838,50.53812 0,-6.74201 2.14819,-0.231343 4.031986,6.973353 4.065036,0 -5.023459,-8.791052 4.759066,-7.733482 -4.031986,0 -3.89979,6.444568 -2.049043,0.198295 0,-13.517069 -3.602348,0 0,23.39874 3.602348,0 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4891"
inkscape:connector-curvature="0" />
<path
d="m 66.269024,50.53812 3.602349,0 0,-16.524534 -3.602349,0 0,16.524534 z m 0,-19.333705 3.602349,0 0,-3.800643 -3.602349,0 0,3.800643 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4893"
inkscape:connector-curvature="0" />
<path
d="m 81.973529,33.650046 c -1.850747,0 -4.131133,0.991472 -4.131133,0.991472 l 0,-7.502138 -3.5693,0 0,23.365691 c 0,0 4.296379,0.396589 6.04798,0.396589 5.915783,0 8.063973,-2.015993 8.063973,-8.85715 0,-6.180176 -1.916846,-8.394464 -6.41152,-8.394464 z M 80.321076,47.6959 c -0.660981,0 -2.47868,-0.132196 -2.47868,-0.132196 l 0,-10.046917 c 0,0 1.949895,-0.660981 3.701495,-0.660981 2.214288,0 3.172711,1.454159 3.172711,5.188704 0,4.031986 -0.727079,5.65139 -4.395526,5.65139 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4895"
inkscape:connector-curvature="0" />
<path
d="m 104.18766,39.103143 c 0,-3.767594 -1.65245,-5.453097 -5.684435,-5.453097 -3.040515,0 -6.642863,0.859276 -6.642863,0.859276 l 0.132196,2.544778 c 0,0 3.998938,-0.33049 6.213225,-0.33049 1.619405,0 2.412587,0.561834 2.412587,2.379533 l 0,1.189766 -4.263335,0.36354 c -3.53625,0.297441 -5.453096,1.487208 -5.453096,4.990409 0,3.437103 1.652453,5.254802 4.924311,5.254802 2.676975,0 5.3209,-1.222816 5.3209,-1.222816 1.22282,0.958423 2.37953,1.222816 4.39553,1.222816 l 0.0991,-2.743073 c -0.95842,-0.132196 -1.38806,-0.528785 -1.45416,-1.520257 l 0,-7.535187 z m -3.56929,3.734544 0,4.395526 c 0,0 -2.214292,0.72708 -4.098089,0.72708 -1.388061,0 -2.015993,-0.925374 -2.015993,-2.412582 0,-1.487208 0.760128,-2.214288 2.280385,-2.346484 l 3.833697,-0.36354 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4897"
inkscape:connector-curvature="0" />
<path
d="m 112.28004,50.53812 0,-12.823038 c 0,0 1.8177,-0.859276 3.73454,-0.859276 2.51173,0 2.94137,1.619404 2.94137,4.924311 l 0,8.758003 3.5693,0 0,-8.85715 c 0,-5.420047 -1.12367,-8.030924 -5.71749,-8.030924 -2.14819,0 -4.56077,1.388061 -4.56077,1.388061 l 0,-1.024521 -3.5693,0 0,16.524534 3.60235,0 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4899"
inkscape:connector-curvature="0" />
<path
d="m 138.91501,39.103143 c 0,-3.767594 -1.65246,-5.453097 -5.68444,-5.453097 -3.04052,0 -6.64287,0.859276 -6.64287,0.859276 l 0.1322,2.544778 c 0,0 3.99894,-0.33049 6.21322,-0.33049 1.61941,0 2.41259,0.561834 2.41259,2.379533 l 0,1.189766 -4.26333,0.36354 c -3.53625,0.297441 -5.4531,1.487208 -5.4531,4.990409 0,3.437103 1.65245,5.254802 4.92431,5.254802 2.67698,0 5.3209,-1.222816 5.3209,-1.222816 1.22282,0.958423 2.37953,1.222816 4.39553,1.222816 l 0.0991,-2.743073 c -0.95842,-0.132196 -1.38806,-0.528785 -1.45415,-1.520257 l 0,-7.535187 z m -3.5693,3.734544 0,4.395526 c 0,0 -2.21429,0.72708 -4.09809,0.72708 -1.38806,0 -2.01599,-0.925374 -2.01599,-2.412582 0,-1.487208 0.76013,-2.214288 2.28038,-2.346484 l 3.8337,-0.36354 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4901"
inkscape:connector-curvature="0" />
</g>
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" inkscape:version="0.91 r13725" sodipodi:docname="kibana.svg" x="0px" y="0px" viewBox="0 0 141.4 51" style="enable-background:new 0 0 141.4 51;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;fill-opacity:0.9253;}
.st1{opacity:0.7;fill:#FFFFFF;}
.st2{opacity:0.5;fill:#FFFFFF;}
.st3{fill:#FFFFFF;}
</style>
<sodipodi:namedview bordercolor="#666666" borderopacity="1" gridtolerance="10" guidetolerance="10" id="namedview29" inkscape:current-layer="Layer_1" inkscape:cx="70.684998" inkscape:cy="25.5" inkscape:pageopacity="1" inkscape:pageshadow="2" inkscape:window-height="1079" inkscape:window-maximized="0" inkscape:window-width="1796" inkscape:window-x="349" inkscape:window-y="210" inkscape:zoom="2.0725756" objecttolerance="10" pagecolor="#515151" showgrid="false">
</sodipodi:namedview>
<title id="title7">Kibana-Full-Logo</title>
<g id="text4159" transform="translate(0,-13.509761)">
<path id="path4891" inkscape:connector-curvature="0" class="st0" d="M53.3,50.5v-6.7l2.1-0.2l4,7h4.1l-5-8.8l4.8-7.7h-4l-3.9,6.4 l-2,0.2V27.1h-3.6v23.4C49.7,50.5,53.3,50.5,53.3,50.5z"/>
<path id="path4893" inkscape:connector-curvature="0" class="st0" d="M66.3,50.5h3.6V34h-3.6C66.3,34,66.3,50.5,66.3,50.5z M66.3,31.2h3.6v-3.8h-3.6V31.2z"/>
<path id="path4895" inkscape:connector-curvature="0" class="st0" d="M82,33.7c-1.9,0-4.1,1-4.1,1v-7.5h-3.6v23.4 c0,0,4.3,0.4,6,0.4c5.9,0,8.1-2,8.1-8.9C88.4,35.9,86.5,33.7,82,33.7z M80.3,47.7c-0.7,0-2.5-0.1-2.5-0.1v-10c0,0,1.9-0.7,3.7-0.7 c2.2,0,3.2,1.5,3.2,5.2C84.7,46.1,84,47.7,80.3,47.7z"/>
<path id="path4897" inkscape:connector-curvature="0" class="st0" d="M104.2,39.1c0-3.8-1.7-5.5-5.7-5.5c-3,0-6.6,0.9-6.6,0.9 l0.1,2.5c0,0,4-0.3,6.2-0.3c1.6,0,2.4,0.6,2.4,2.4v1.2l-4.3,0.4c-3.5,0.3-5.5,1.5-5.5,5c0,3.4,1.7,5.3,4.9,5.3 c2.7,0,5.3-1.2,5.3-1.2c1.2,1,2.4,1.2,4.4,1.2l0.1-2.7c-1-0.1-1.4-0.5-1.5-1.5L104.2,39.1L104.2,39.1z M100.6,42.8v4.4 c0,0-2.2,0.7-4.1,0.7c-1.4,0-2-0.9-2-2.4s0.8-2.2,2.3-2.3L100.6,42.8z"/>
<path id="path4899" inkscape:connector-curvature="0" class="st0" d="M112.3,50.5V37.7c0,0,1.8-0.9,3.7-0.9c2.5,0,2.9,1.6,2.9,4.9 v8.8h3.6v-8.9c0-5.4-1.1-8-5.7-8c-2.1,0-4.6,1.4-4.6,1.4v-1h-3.6v16.5C108.7,50.5,112.3,50.5,112.3,50.5z"/>
<path id="path4901" inkscape:connector-curvature="0" class="st0" d="M138.9,39.1c0-3.8-1.7-5.5-5.7-5.5c-3,0-6.6,0.9-6.6,0.9 l0.1,2.5c0,0,4-0.3,6.2-0.3c1.6,0,2.4,0.6,2.4,2.4v1.2l-4.3,0.4c-3.5,0.3-5.5,1.5-5.5,5c0,3.4,1.7,5.3,4.9,5.3 c2.7,0,5.3-1.2,5.3-1.2c1.2,1,2.4,1.2,4.4,1.2l0.1-2.7c-1-0.1-1.4-0.5-1.5-1.5L138.9,39.1L138.9,39.1z M135.3,42.8v4.4 c0,0-2.2,0.7-4.1,0.7c-1.4,0-2-0.9-2-2.4s0.8-2.2,2.3-2.3L135.3,42.8z"/>
</g>
<g>
<path class="st1" d="M19.4,23.8L39.3,0H0v19.1c0.2,0,0.5,0,0.7,0C7.5,19.1,13.9,20.8,19.4,23.8z"/>
<polygon class="st1" points="0,47.3 0,47.3 19.4,23.8 "/>
<path class="st1" d="M24.5,27.1c0.2,0.2,0.4,0.3,0.5,0.4C24.9,27.4,24.7,27.3,24.5,27.1z"/>
<path class="st1" d="M23,26c0.1,0.1,0.2,0.1,0.3,0.2C23.2,26.2,23.1,26.1,23,26z"/>
<path class="st1" d="M25.8,28.2L7,51h32.2C37.5,41.9,32.7,33.9,25.8,28.2z"/>
<path class="st2" d="M22.8,25.9c-1.4-1-3.4-2.1-3.4-2.1c1.2,0.7,2.4,1.4,3.6,2.2C22.9,26,22.9,26,22.8,25.9z"/>
<path class="st2" d="M25.9,28.2c0,0-0.3-0.2-0.8-0.6C25.3,27.8,25.6,28,25.9,28.2L25.9,28.2z"/>
<path class="st2" d="M23.3,26.3c0.4,0.3,0.8,0.6,1.2,0.9C24.2,26.9,23.8,26.6,23.3,26.3z"/>
<path class="st2" d="M25.1,27.6c-0.2-0.1-0.3-0.3-0.5-0.4c-0.4-0.3-0.8-0.6-1.2-0.9c-0.1-0.1-0.2-0.1-0.3-0.2 c-1.1-0.8-2.3-1.5-3.6-2.2L0,47.3V51h7l18.9-22.8C25.6,28,25.3,27.8,25.1,27.6z"/>
<path class="st3" d="M0.7,19.1c-0.2,0-0.5,0-0.7,0v28.2l19.4-23.4C13.9,20.8,7.5,19.1,0.7,19.1z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Before After
Before After

View file

@ -87,8 +87,9 @@ export default function ($compile) {
}
const $childScope = $scope.$new();
const $el = $compile(templateToRender)($childScope);
$element.find('#template_wrapper').html($el);
const $el = $element.find('#template_wrapper').html(templateToRender).contents();
$compile($el)($childScope);
this.rendered = { $childScope, $el, key: currentKey };
}
};

View file

@ -49,20 +49,19 @@ if (!!kbnIndex) {
});
}
/**
* Global Angular exception handler (NOT JUST UNCAUGHT EXCEPTIONS)
*/
// modules
// .get('exceptionOverride')
// .factory('$exceptionHandler', function () {
// return function (exception, cause) {
// rootNotifier.fatal(exception, cause);
// };
// });
window.onerror = function (err, url, line) {
rootNotifier.fatal(new Error(err + ' (' + url + ':' + line + ')'));
return true;
};
if (window.addEventListener) {
const notify = new Notifier({
location: 'Promise'
});
window.addEventListener('unhandledrejection', function (e) {
notify.log(`Detected an unhandled Promise rejection.\n${e.reason}`);
});
}
export default rootNotifier;

View file

@ -47,12 +47,13 @@ describe('paginated table', function () {
};
};
let renderTable = function (cols, rows, perPage) {
let renderTable = function (cols, rows, perPage, sort) {
$scope.cols = cols || [];
$scope.rows = rows || [];
$scope.perPage = perPage || defaultPerPage;
$scope.sort = sort || {};
$el = $compile('<paginated-table columns="cols" rows="rows" per-page="perPage">')($scope);
$el = $compile('<paginated-table columns="cols" rows="rows" per-page="perPage" sort="sort">')($scope);
$scope.$digest();
};
@ -160,6 +161,26 @@ describe('paginated table', function () {
expect(tableRows.eq(0).find('td').eq(2).text()).to.be('zzzz');
});
it('should set the sort direction to asc when it\'s not explicity set', function () {
paginatedTable.sortColumn(1);
$scope.$digest();
var tableRows = $el.find('tbody tr');
expect(tableRows.eq(2).find('td').eq(1).text()).to.be('cccc');
expect(tableRows.eq(1).find('td').eq(1).text()).to.be('bbbb');
expect(tableRows.eq(0).find('td').eq(1).text()).to.be('aaaa');
});
it('should allow you to explicitly set the sort direction', function () {
paginatedTable.sortColumn(1, 'desc');
$scope.$digest();
var tableRows = $el.find('tbody tr');
expect(tableRows.eq(0).find('td').eq(1).text()).to.be('zzzz');
expect(tableRows.eq(1).find('td').eq(1).text()).to.be('cccc');
expect(tableRows.eq(2).find('td').eq(1).text()).to.be('bbbb');
});
it('should sort ascending on first invocation', function () {
// sortColumn
paginatedTable.sortColumn(0);
@ -289,35 +310,6 @@ describe('paginated table', function () {
});
describe('custom sorting', function () {
let data;
let paginatedTable;
let sortHandler;
beforeEach(function () {
sortHandler = sinon.spy();
data = makeData(3, 3);
$scope.cols = data.columns;
$scope.rows = data.rows;
$scope.perPage = defaultPerPage;
$scope.sortHandler = sortHandler;
$el = $compile('<paginated-table columns="cols" rows="rows" per-page="perPage"' +
'sort-handler="sortHandler">')($scope);
$scope.$digest();
paginatedTable = $el.isolateScope().paginatedTable;
});
// TODO: This is failing randomly
it('should allow custom sorting handler', function () {
let columnIndex = 1;
paginatedTable.sortColumn(columnIndex);
$scope.$digest();
expect(sortHandler.callCount).to.be(1);
expect(sortHandler.getCall(0).args[0]).to.be(columnIndex);
});
});
describe('object rows', function () {
let cols;

View file

@ -1,10 +1,11 @@
import _ from 'lodash';
import uiModules from 'ui/modules';
import paginatedTableTemplate from 'ui/paginated_table/paginated_table.html';
uiModules
.get('kibana')
.directive('paginatedTable', function ($filter) {
let orderBy = $filter('orderBy');
const orderBy = $filter('orderBy');
return {
restrict: 'E',
@ -15,28 +16,25 @@ uiModules
columns: '=',
perPage: '=?',
sortHandler: '=?',
sort: '=?',
showSelector: '=?'
},
controllerAs: 'paginatedTable',
controller: function ($scope) {
let self = this;
const self = this;
self.sort = {
columnIndex: null,
direction: null
};
self.sortColumn = function (colIndex) {
let col = $scope.columns[colIndex];
self.sortColumn = function (colIndex, sortDirection = 'asc') {
const col = $scope.columns[colIndex];
if (!col) return;
if (col.sortable === false) return;
let sortDirection;
if (self.sort.columnIndex !== colIndex) {
sortDirection = 'asc';
} else {
let directions = {
if (self.sort.columnIndex === colIndex) {
const directions = {
null: 'asc',
'asc': 'desc',
'desc': null
@ -46,42 +44,49 @@ uiModules
self.sort.columnIndex = colIndex;
self.sort.direction = sortDirection;
self._setSortGetter(colIndex);
};
self._setSortGetter = function (index) {
if (_.isFunction($scope.sortHandler)) {
// use custom sort handler
self.sort.getter = $scope.sortHandler(index);
} else {
// use generic sort handler
self.sort.getter = function (row) {
let value = row[index];
if (value && value.value != null) value = value.value;
if (typeof value === 'boolean') value = value ? 0 : 1;
return value;
};
if ($scope.sort) {
_.assign($scope.sort, self.sort);
}
};
// update the sordedRows result
$scope.$watchMulti([
'rows',
'columns',
'[]paginatedTable.sort'
], function resortRows() {
function valueGetter(row) {
let value = row[self.sort.columnIndex];
if (value && value.value != null) value = value.value;
if (typeof value === 'boolean') value = value ? 0 : 1;
return value;
}
// Set the sort state if it is set
if ($scope.sort && $scope.sort.columnIndex !== null) {
self.sortColumn($scope.sort.columnIndex, $scope.sort.direction);
}
function resortRows() {
const newSort = $scope.sort;
if (newSort && !_.isEqual(newSort, self.sort)) {
self.sortColumn(newSort.columnIndex, newSort.direction);
}
if (!$scope.rows || !$scope.columns) {
$scope.sortedRows = false;
return;
}
let sort = self.sort;
const sort = self.sort;
if (sort.direction == null) {
$scope.sortedRows = $scope.rows.slice(0);
} else {
$scope.sortedRows = orderBy($scope.rows, sort.getter, sort.direction === 'desc');
$scope.sortedRows = orderBy($scope.rows, valueGetter, sort.direction === 'desc');
}
});
}
// update the sortedRows result
$scope.$watchMulti([
'rows',
'columns',
'[]sort',
'[]paginatedTable.sort'
], resortRows);
}
};
});

View file

@ -4,10 +4,11 @@ import applyDiff from 'ui/utils/diff_object';
import qs from 'ui/utils/query_string';
import EventsProvider from 'ui/events';
import Notifier from 'ui/notify/notifier';
import KbnUrlProvider from 'ui/url';
const notify = new Notifier();
export default function StateProvider(Private, $rootScope, $location) {
let Events = Private(EventsProvider);
const Events = Private(EventsProvider);
_.class(State).inherits(Events);
function State(urlParam, defaults) {
@ -27,10 +28,14 @@ export default function StateProvider(Private, $rootScope, $location) {
// beginning of full route update, new app will be initialized before
// $routeChangeSuccess or $routeChangeError
$rootScope.$on('$routeChangeStart', function () {
if (!self._persistAcrossApps) {
self.destroy();
}
}),
$rootScope.$on('$routeChangeSuccess', function () {
if (self._persistAcrossApps) {
self.fetch();
} else {
self.destroy();
}
})
]);
@ -44,7 +49,6 @@ export default function StateProvider(Private, $rootScope, $location) {
try {
return search[this._urlParam] ? rison.decode(search[this._urlParam]) : null;
} catch (e) {
let notify = new Notifier();
notify.error('Unable to parse URL');
search[this._urlParam] = rison.encode(this._defaults);
$location.search(search).replace();

View file

@ -9,6 +9,7 @@ export default function TemplateRenderbotFactory(Private, $compile, $rootScope)
this.$scope = $rootScope.$new();
this.$scope.vis = vis;
this.$scope.uiState = uiState;
$el.html($compile(this.vis.type.template)(this.$scope));
}

View file

@ -1,5 +1,5 @@
import moment from 'moment';
import dateMath from 'ui/utils/date_math';
import dateMath from '@elastic/datemath';
export default function () {
let unitsDesc = dateMath.unitsDesc;

View file

@ -1,6 +1,6 @@
import _ from 'lodash';
import moment from 'moment';
import dateMath from 'ui/utils/date_math';
import dateMath from '@elastic/datemath';
import parseInterval from 'ui/utils/parse_interval';
import TimeBucketsCalcAutoIntervalProvider from 'ui/time_buckets/calc_auto_interval';
import TimeBucketsCalcEsIntervalProvider from 'ui/time_buckets/calc_es_interval';

View file

@ -1,7 +1,7 @@
import _ from 'lodash';
import angular from 'angular';
import moment from 'moment';
import dateMath from 'ui/utils/date_math';
import dateMath from '@elastic/datemath';
import 'ui/state_management/global_state';
import 'ui/config';
import EventsProvider from 'ui/events';
@ -19,11 +19,9 @@ uiRoutes
uiModules
.get('kibana')
.service('timefilter', function (Private, globalState, $rootScope, config) {
let Events = Private(EventsProvider);
let diff = Private(UtilsDiffTimePickerValsProvider);
function convertISO8601(stringTime) {
let obj = moment(stringTime, 'YYYY-MM-DDTHH:mm:ss.SSSZ', true);
return obj.isValid() ? obj : stringTime;

View file

@ -1,3 +1,4 @@
import moment from 'moment';
import UiModules from 'ui/modules';
import chromeNavControlsRegistry from 'ui/registry/chrome_nav_controls';
import { once, clone } from 'lodash';
@ -6,7 +7,7 @@ import toggleHtml from './kbn_global_timepicker.html';
UiModules
.get('kibana')
.directive('kbnGlobalTimepicker', (timefilter, globalState, $rootScope) => {
.directive('kbnGlobalTimepicker', (timefilter, globalState, $rootScope, config) => {
const listenForUpdates = once($scope => {
$scope.$listen(timefilter, 'update', (newVal, oldVal) => {
globalState.time = clone(timefilter.time);
@ -18,6 +19,12 @@ UiModules
return {
template: toggleHtml,
link: ($scope, $el, attrs) => {
config.$bind($scope, 'dateFormat:dow', 'dateFormat_dow');
$scope.$watch('dateFormat_dow', function (day) {
const dow = moment.weekdays().indexOf(day);
moment.locale(moment.locale(), { week: { dow } });
});
listenForUpdates($rootScope);
$rootScope.timefilter = timefilter;

View file

@ -1,6 +1,6 @@
import html from 'ui/timepicker/timepicker.html';
import _ from 'lodash';
import dateMath from 'ui/utils/date_math';
import dateMath from '@elastic/datemath';
import moment from 'moment';
import Notifier from 'ui/notify/notifier';
import 'ui/directives/input_datetime';
@ -15,7 +15,6 @@ let notify = new Notifier({
location: 'timepicker',
});
module.directive('kbnTimepicker', function (quickRanges, timeUnits, refreshIntervals) {
return {
restrict: 'E',
@ -28,10 +27,6 @@ module.directive('kbnTimepicker', function (quickRanges, timeUnits, refreshInter
},
template: html,
controller: function ($scope) {
let init = function () {
$scope.setMode($scope.mode);
};
$scope.format = 'MMMM Do YYYY, HH:mm:ss.SSS';
$scope.modes = ['quick', 'relative', 'absolute'];
$scope.activeTab = $scope.activeTab || 'filter';
@ -115,7 +110,7 @@ module.directive('kbnTimepicker', function (quickRanges, timeUnits, refreshInter
$scope.mode = thisMode;
};
$scope.setQuick = function (from, to, description) {
$scope.setQuick = function (from, to) {
$scope.from = from;
$scope.to = to;
};
@ -154,7 +149,7 @@ module.directive('kbnTimepicker', function (quickRanges, timeUnits, refreshInter
$scope.interval = interval;
};
init();
$scope.setMode($scope.mode);
}
};
});

View file

@ -1,143 +0,0 @@
import dateMath from 'ui/utils/date_math';
import expect from 'expect.js';
import moment from 'moment';
import _ from 'lodash';
import sinon from 'auto-release-sinon';
describe('dateMath', function () {
// Test each of these intervals when testing relative time
let spans = ['s', 'm', 'h', 'd', 'w', 'M', 'y'];
let anchor = '2014-01-01T06:06:06.666Z';
let unix = moment(anchor).valueOf();
let format = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
let clock;
describe('errors', function () {
it('should return undefined if passed something falsy', function () {
expect(dateMath.parse()).to.be(undefined);
});
it('should return undefined if I pass an operator besides [+-/]', function () {
expect(dateMath.parse('now&1d')).to.be(undefined);
});
it('should return undefined if I pass a unit besides' + spans.toString(), function () {
expect(dateMath.parse('now+5f')).to.be(undefined);
});
it('should return undefined if rounding unit is not 1', function () {
expect(dateMath.parse('now/2y')).to.be(undefined);
expect(dateMath.parse('now/0.5y')).to.be(undefined);
});
it('should not go into an infinite loop when missing a unit', function () {
expect(dateMath.parse('now-0')).to.be(undefined);
expect(dateMath.parse('now-00')).to.be(undefined);
expect(dateMath.parse('now-000')).to.be(undefined);
});
});
describe('objects and strings', function () {
let mmnt;
let date;
let string;
let now;
beforeEach(function () {
clock = sinon.useFakeTimers(unix);
now = moment();
mmnt = moment(anchor);
date = mmnt.toDate();
string = mmnt.format(format);
});
it('should return the same moment if passed a moment', function () {
expect(dateMath.parse(mmnt)).to.eql(mmnt);
});
it('should return a moment if passed a date', function () {
expect(dateMath.parse(date).format(format)).to.eql(mmnt.format(format));
});
it('should return a moment if passed an ISO8601 string', function () {
expect(dateMath.parse(string).format(format)).to.eql(mmnt.format(format));
});
it('should return the current time if passed now', function () {
expect(dateMath.parse('now').format(format)).to.eql(now.format(format));
});
});
describe('subtraction', function () {
let now;
let anchored;
beforeEach(function () {
clock = sinon.useFakeTimers(unix);
now = moment();
anchored = moment(anchor);
});
_.each(spans, function (span) {
let nowEx = 'now-5' + span;
let thenEx = anchor + '||-5' + span;
it('should return 5' + span + ' ago', function () {
expect(dateMath.parse(nowEx).format(format)).to.eql(now.subtract(5, span).format(format));
});
it('should return 5' + span + ' before ' + anchor, function () {
expect(dateMath.parse(thenEx).format(format)).to.eql(anchored.subtract(5, span).format(format));
});
});
});
describe('addition', function () {
let now;
let anchored;
beforeEach(function () {
clock = sinon.useFakeTimers(unix);
now = moment();
anchored = moment(anchor);
});
_.each(spans, function (span) {
let nowEx = 'now+5' + span;
let thenEx = anchor + '||+5' + span;
it('should return 5' + span + ' from now', function () {
expect(dateMath.parse(nowEx).format()).to.eql(now.add(5, span).format());
});
it('should return 5' + span + ' after ' + anchor, function () {
expect(dateMath.parse(thenEx).format()).to.eql(anchored.add(5, span).format());
});
});
});
describe('rounding', function () {
let now;
let anchored;
beforeEach(function () {
clock = sinon.useFakeTimers(unix);
now = moment();
anchored = moment(anchor);
});
_.each(spans, function (span) {
it('should round now to the beginning of the ' + span, function () {
expect(dateMath.parse('now/' + span).format(format)).to.eql(now.startOf(span).format(format));
});
it('should round now to the end of the ' + span, function () {
expect(dateMath.parse('now/' + span, true).format(format)).to.eql(now.endOf(span).format(format));
});
});
});
});

View file

@ -53,9 +53,22 @@ describe('Scanner', function () {
scroll = sinon.stub(scanner.client, 'scroll', (req, cb) => cb(null, mockScroll));
});
it('should reject when an error occurs', function (done) {
search.restore();
search = sinon.stub(scanner.client, 'search', (req, cb) => cb(new Error('fail.')));
return scanner.scanAndMap('')
.then(function (response) {
done(new Error('should reject'));
})
.catch(function (error) {
expect(error.message).to.be('fail.');
done();
});
});
it('should search and then scroll for results', function () {
return scanner.scanAndMap('')
.then(function (error, response) {
.then(function (response) {
expect(search.called).to.be(true);
expect(scroll.called).to.be(true);
});

View file

@ -1,108 +0,0 @@
import _ from 'lodash';
import moment from 'moment';
let units = ['y', 'M', 'w', 'd', 'h', 'm', 's', 'ms'];
let unitsAsc = _.sortBy(units, function (unit) {
return moment.duration(1, unit).valueOf();
});
let unitsDesc = unitsAsc.reverse();
/* This is a simplified version of elasticsearch's date parser */
function parse(text, roundUp) {
if (!text) return undefined;
if (moment.isMoment(text)) return text;
if (_.isDate(text)) return moment(text);
let time;
let mathString = '';
let index;
let parseString;
if (text.substring(0, 3) === 'now') {
time = moment();
mathString = text.substring('now'.length);
} else {
index = text.indexOf('||');
if (index === -1) {
parseString = text;
mathString = ''; // nothing else
} else {
parseString = text.substring(0, index);
mathString = text.substring(index + 2);
}
// We're going to just require ISO8601 timestamps, k?
time = moment(parseString);
}
if (!mathString.length) {
return time;
}
return parseDateMath(mathString, time, roundUp);
}
function parseDateMath(mathString, time, roundUp) {
let dateTime = time;
let i = 0;
let len = mathString.length;
while (i < len) {
let c = mathString.charAt(i++);
let type;
let num;
let unit;
if (c === '/') {
type = 0;
} else if (c === '+') {
type = 1;
} else if (c === '-') {
type = 2;
} else {
return undefined;
}
if (isNaN(mathString.charAt(i))) {
num = 1;
} else if (mathString.length === 2) {
num = mathString.charAt(i);
} else {
let numFrom = i;
while (!isNaN(mathString.charAt(i))) {
i++;
if (i > 10) return undefined;
}
num = parseInt(mathString.substring(numFrom, i), 10);
}
if (type === 0) {
// rounding is only allowed on whole, single, units (eg M or 1M, not 0.5M or 2M)
if (num !== 1) {
return undefined;
}
}
unit = mathString.charAt(i++);
if (!_.contains(units, unit)) {
return undefined;
} else {
if (type === 0) {
if (roundUp) dateTime.endOf(unit);
else dateTime.startOf(unit);
} else if (type === 1) {
dateTime.add(num, unit);
} else if (type === 2) {
dateTime.subtract(num, unit);
}
}
}
return dateTime;
}
export default {
parse: parse,
units: Object.freeze(units),
unitsAsc: Object.freeze(unitsAsc),
unitsDesc: Object.freeze(unitsDesc)
};

View file

@ -1,6 +1,6 @@
import _ from 'lodash';
import moment from 'moment';
import dateMath from 'ui/utils/date_math';
import dateMath from '@elastic/datemath';
// Assume interval is in the form (value)(unit), such as "1h"
let INTERVAL_STRING_RE = new RegExp('^([0-9\\.]*)\\s*(' + dateMath.units.join('|') + ')$');

View file

@ -38,6 +38,10 @@ Scanner.prototype.scanAndMap = function (searchString, options, mapFn) {
return new Promise((resolve, reject) => {
const getMoreUntilDone = (error, response) => {
if (error) {
reject(error);
return;
}
const scanAllResults = opts.docCount === Infinity;
allResults.total = scanAllResults ? response.hits.total : Math.min(response.hits.total, opts.docCount);
scrollId = response._scroll_id || scrollId;

View file

@ -232,17 +232,25 @@ export default function MapFactory(Private) {
// TODO: Different drawTypes need differ info. Need a switch on the object creation
let bounds = e.layer.getBounds();
let SElng = bounds.getSouthEast().lng;
if (SElng > 180) {
SElng -= 360;
}
let NWlng = bounds.getNorthWest().lng;
if (NWlng < -180) {
NWlng += 360;
}
self._events.emit(drawType, {
e: e,
chart: self._chartData,
bounds: {
top_left: {
lat: bounds.getNorthWest().lat,
lon: bounds.getNorthWest().lng
lon: NWlng
},
bottom_right: {
lat: bounds.getSouthEast().lat,
lon: bounds.getSouthEast().lng
lon: SElng
}
}
});

View file

@ -1,5 +1,5 @@
import d3 from 'd3';
import dateMath from 'ui/utils/date_math';
import dateMath from '@elastic/datemath';
export default function TimeMarkerFactory() {
function TimeMarker(times, xScale, height) {

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