mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Merge pull request #278 from spenceralger/master
WIP changes for incomming PR
This commit is contained in:
commit
3db112657c
19 changed files with 764 additions and 24 deletions
2
TODOS.md
2
TODOS.md
|
@ -10,8 +10,6 @@
|
|||
- a legit way to update the index pattern
|
||||
- **[src/kibana/apps/settings/sections/indices/_create.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/apps/settings/sections/indices/_create.js)**
|
||||
- we should probably display a message of some kind
|
||||
- **[src/kibana/components/agg_types/buckets/terms.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/components/agg_types/buckets/terms.js)**
|
||||
- We need more than just _count here.
|
||||
- **[src/kibana/components/index_patterns/_mapper.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/components/index_patterns/_mapper.js)**
|
||||
- Change index to be the resolved in some way, last three months, last hour, last year, whatever
|
||||
- **[src/kibana/components/visualize/visualize.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/components/visualize/visualize.js)**
|
||||
|
|
|
@ -10,9 +10,9 @@ Collection of `AggType` definition objects. See the [Vis component](../vis) for
|
|||
|
||||
### Included
|
||||
|
||||
- [`AggType`](_agg_type.js) class
|
||||
- `AggParam` classes
|
||||
- [`AggType`](_agg_type.js)
|
||||
- `AggParam`
|
||||
- [`BaseAggParam`](param_types/base.js)
|
||||
- [`FieldAggParam`](param_types/field.js)
|
||||
- [`OptionedAggParam`](param_types/optioned.js)
|
||||
- [`AggParams`](_agg_params.js) class
|
||||
- [`AggParams`](_agg_params.js)
|
|
@ -7,11 +7,31 @@ define(function (require) {
|
|||
var FieldAggParam = Private(require('components/agg_types/param_types/field'));
|
||||
var OptionedAggParam = Private(require('components/agg_types/param_types/optioned'));
|
||||
|
||||
/**
|
||||
* Wraps a list of {{#crossLink "AggParam"}}{{/crossLink}} objects; owned by an {{#crossLink "AggType"}}{{/crossLink}}
|
||||
*
|
||||
* used to create:
|
||||
* - `OptionedAggParam` – When the config has an array of `options: []`
|
||||
* - `FieldAggParam` – When the config has `name: "field"`
|
||||
* - `BaseAggParam` – All other params
|
||||
*
|
||||
* @class AggParams
|
||||
* @constructor
|
||||
* @extends Registry
|
||||
* @param {object[]} params - array of params that get new-ed up as AggParam objects as descibed above
|
||||
*/
|
||||
_(AggParams).inherits(Registry);
|
||||
function AggParams(params) {
|
||||
if (_.isPlainObject(params)) {
|
||||
// convert the names: details format into details[].name
|
||||
params = _.map(params, function (param, name) {
|
||||
param.name = name;
|
||||
return param;
|
||||
});
|
||||
}
|
||||
|
||||
AggParams.Super.call(this, {
|
||||
index: ['name'],
|
||||
group: ['required'],
|
||||
initialSet: params.map(function (param) {
|
||||
if (param.name === 'field') {
|
||||
return new FieldAggParam(param);
|
||||
|
@ -26,6 +46,20 @@ define(function (require) {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an aggConfigs
|
||||
*
|
||||
* @method write
|
||||
* @param {AggConfig} aggConfig
|
||||
* the AggConfig object who's type owns these aggParams and contains the param values for our param defs
|
||||
* @param {object} [locals]
|
||||
* an array of locals that will be available to the write function (can be used to enhance
|
||||
* the quality of things like date_histogram's "auto" interval)
|
||||
* @return {object} output
|
||||
* output of the write calls, reduced into a single object. A `params: {}` property is exposed on the
|
||||
* output object which is used to create the agg DSL for the search request. All other properties
|
||||
* are dependent on the AggParam#write methods which should be studied for each AggType.
|
||||
*/
|
||||
AggParams.prototype.write = function (aggConfig, locals) {
|
||||
var output = { params: {} };
|
||||
locals = locals || {};
|
||||
|
|
|
@ -3,23 +3,67 @@ define(function (require) {
|
|||
var _ = require('lodash');
|
||||
var AggParams = Private(require('components/agg_types/_agg_params'));
|
||||
|
||||
/**
|
||||
* Generic AggType Constructor
|
||||
*
|
||||
* Used to create the values exposed by the agg_types module.
|
||||
*
|
||||
* @class AggType
|
||||
* @private
|
||||
* @param {object} config - used to set the properties of the AggType
|
||||
*/
|
||||
function AggType(config) {
|
||||
|
||||
/**
|
||||
* the unique, unchanging, name that elasticsearch has assigned this aggType
|
||||
*
|
||||
* @property name
|
||||
* @type {string}
|
||||
*/
|
||||
this.name = config.name;
|
||||
|
||||
/**
|
||||
* the user friendly name that will be shown in the ui for this aggType
|
||||
*
|
||||
* @property title
|
||||
* @type {string}
|
||||
*/
|
||||
this.title = config.title;
|
||||
|
||||
/**
|
||||
* a function that will be called when this aggType is assigned to
|
||||
* an aggConfig, and that aggConfig is being rendered (in a form, chart, etc.).
|
||||
*
|
||||
* @method makeLabel
|
||||
* @param {AggConfig} aggConfig - an agg config of this type
|
||||
* @returns {string} - label that can be used in the ui to descripe the aggConfig
|
||||
*/
|
||||
this.makeLabel = config.makeLabel || _.constant(this.name);
|
||||
|
||||
/**
|
||||
* Describes if this aggType creates data that is ordered, and if that ordered data
|
||||
* is some sort of time series.
|
||||
*
|
||||
* If the aggType does not create ordered data, set this to something "falsey".
|
||||
*
|
||||
* If this does create orderedData, then the value should be an object.
|
||||
*
|
||||
* If the orderdata is some sort of time series, `this.ordered` should be an object
|
||||
* with the property `date: true`
|
||||
*
|
||||
* @property ordered
|
||||
* @type {object|undefined}
|
||||
*/
|
||||
this.ordered = config.ordered;
|
||||
|
||||
/**
|
||||
* An instance of {{#crossLink "AggParams"}}{{/crossLink}}.
|
||||
*
|
||||
* @property params
|
||||
* @type {AggParams}
|
||||
*/
|
||||
var params = this.params = config.params || [];
|
||||
|
||||
if (!(params instanceof AggParams)) {
|
||||
if (_.isPlainObject(params)) {
|
||||
// convert the names: details format into details[].name
|
||||
params = _.map(params, function (param, name) {
|
||||
param.name = name;
|
||||
return param;
|
||||
});
|
||||
}
|
||||
|
||||
params = this.params = new AggParams(params);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,10 +28,11 @@ define(function (require) {
|
|||
editor: require('text!components/agg_types/controls/order_and_size.html'),
|
||||
default: 'desc',
|
||||
write: function (aggConfig, output) {
|
||||
// TODO: We need more than just _count here.
|
||||
output.params.order = {
|
||||
_count: aggConfig.params.order.val
|
||||
};
|
||||
|
||||
var metricAgg = _.first(aggConfig.vis.aggs.bySchemaGroup.metrics);
|
||||
|
||||
output.params.order = {};
|
||||
output.params.order[metricAgg.id] = aggConfig.params.order.val;
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -20,8 +20,26 @@ define(function (require) {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Registry of Aggregation Types.
|
||||
*
|
||||
* These types form two groups, metric and buckets.
|
||||
*
|
||||
* @module agg_types
|
||||
* @type {Registry}
|
||||
*/
|
||||
return new Registry({
|
||||
|
||||
/**
|
||||
* @type {Array}
|
||||
*/
|
||||
index: ['name'],
|
||||
|
||||
/**
|
||||
* [group description]
|
||||
* @type {Array}
|
||||
*/
|
||||
group: ['type'],
|
||||
initialSet: aggs.metrics.concat(aggs.buckets)
|
||||
});
|
||||
|
|
|
@ -25,15 +25,19 @@ define(function (require) {
|
|||
// one cache per instance of the Private service
|
||||
var cache = {};
|
||||
|
||||
function Private(fn) {
|
||||
function identify(fn) {
|
||||
if (typeof fn !== 'function') {
|
||||
throw new TypeError('Expected private module "' + fn + '" to be a function');
|
||||
}
|
||||
|
||||
var id = fn.$$id;
|
||||
if (id && cache[id]) return cache[id];
|
||||
if (fn.$$id) return fn.$$id;
|
||||
else return (fn.$$id = nextId());
|
||||
}
|
||||
|
||||
if (!id) id = fn.$$id = nextId();
|
||||
function Private(fn) {
|
||||
var id = identify(fn);
|
||||
|
||||
if (cache[id]) return cache[id];
|
||||
else if (~privPath.indexOf(id)) {
|
||||
throw new Error(
|
||||
'Circluar refrence to "' + name(fn) + '"' +
|
||||
|
@ -54,6 +58,11 @@ define(function (require) {
|
|||
return instance;
|
||||
}
|
||||
|
||||
Private.stub = function (fn, val) {
|
||||
cache[identify(fn)] = val;
|
||||
return val;
|
||||
};
|
||||
|
||||
return Private;
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,6 +14,9 @@ define(function (require) {
|
|||
* Generic extension of Array class, which will index (and reindex) the
|
||||
* objects it contains based on their properties.
|
||||
*
|
||||
* @class Registry
|
||||
* @module utils
|
||||
* @constructor
|
||||
* @param {object} [config] - describes the properties of this registry object
|
||||
* @param {string[]} [config.index] - a list of props/paths that should be used to index the docs.
|
||||
* @param {string[]} [config.group] - a list of keys/paths to group docs by.
|
||||
|
|
17
test/unit/fixtures/stubbed_logstash_index_pattern.js
Normal file
17
test/unit/fixtures/stubbed_logstash_index_pattern.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
define(function (require) {
|
||||
return function stubbedLogstashIndexPatternService(Private) {
|
||||
var StubIndexPattern = Private(require('test_utils/stub_index_pattern'));
|
||||
return new StubIndexPattern('logstash-*', 'time', [
|
||||
{ type: 'number', name: 'bytes' },
|
||||
{ type: 'boolean', name: 'ssl' },
|
||||
{ type: 'date', name: '@timestamp' },
|
||||
{ type: 'ip', name: 'ip' },
|
||||
{ type: 'attachment', name: 'request_body' },
|
||||
{ type: 'string', name: 'extension' },
|
||||
{ type: 'geo_point', name: 'point' },
|
||||
{ type: 'geo_shape', name: 'area' },
|
||||
{ type: 'string', name: 'extension' },
|
||||
{ type: 'conflict', name: 'custom_user_field' }
|
||||
]);
|
||||
};
|
||||
});
|
|
@ -87,7 +87,8 @@
|
|||
'specs/factories/events',
|
||||
'specs/index_patterns/_flatten_search_response',
|
||||
'specs/utils/registry/index',
|
||||
'specs/directives/filter_bar'
|
||||
'specs/directives/filter_bar',
|
||||
'specs/components/agg_types/index'
|
||||
], function (kibana, sinon) {
|
||||
kibana.load(function () {
|
||||
var xhr = sinon.useFakeXMLHttpRequest();
|
||||
|
|
99
test/unit/specs/components/agg_types/_agg_params.js
Normal file
99
test/unit/specs/components/agg_types/_agg_params.js
Normal file
|
@ -0,0 +1,99 @@
|
|||
define(function (require) {
|
||||
return ['AggParams class', function () {
|
||||
var _ = require('lodash');
|
||||
|
||||
var AggParams;
|
||||
var BaseAggParam;
|
||||
var FieldAggParam;
|
||||
var OptionedAggParam;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
// stub out the param classes before we get the AggParams
|
||||
beforeEach(inject(require('specs/components/agg_types/utils/stub_agg_params')));
|
||||
// fetch out deps
|
||||
beforeEach(inject(function (Private) {
|
||||
AggParams = Private(require('components/agg_types/_agg_params'));
|
||||
BaseAggParam = Private(require('components/agg_types/param_types/base'));
|
||||
FieldAggParam = Private(require('components/agg_types/param_types/field'));
|
||||
OptionedAggParam = Private(require('components/agg_types/param_types/optioned'));
|
||||
}));
|
||||
|
||||
describe('constructor args', function () {
|
||||
it('accepts an object of params defs', function () {
|
||||
var aggParams = new AggParams({
|
||||
one: {},
|
||||
two: {}
|
||||
});
|
||||
|
||||
expect(aggParams).to.have.length(2);
|
||||
expect(aggParams).to.be.an(Array);
|
||||
expect(aggParams.byName).to.have.keys(['one', 'two']);
|
||||
});
|
||||
|
||||
it('accepts an array of param defs', function () {
|
||||
var aggParams = new AggParams([
|
||||
{ name: 'one' },
|
||||
{ name: 'two' }
|
||||
]);
|
||||
|
||||
expect(aggParams).to.have.length(2);
|
||||
expect(aggParams).to.be.an(Array);
|
||||
expect(aggParams.byName).to.have.keys(['one', 'two']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AggParam creation', function () {
|
||||
it('Uses the FieldAggParam class for params with the name "field"', function () {
|
||||
var aggParams = new AggParams([
|
||||
{ name: 'field' }
|
||||
]);
|
||||
|
||||
expect(aggParams).to.have.length(1);
|
||||
expect(aggParams[0]).to.be.a(FieldAggParam);
|
||||
expect(aggParams[0]).to.be.a(BaseAggParam);
|
||||
});
|
||||
|
||||
it('Uses the OptionedAggParam class for params with defined options', function () {
|
||||
var aggParams = new AggParams([
|
||||
{
|
||||
name: 'interval',
|
||||
options: [
|
||||
{ display: 'Automatic', val: 'auto' },
|
||||
{ display: '2 Hours', val: '2h' }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
expect(aggParams).to.have.length(1);
|
||||
expect(aggParams[0]).to.be.a(OptionedAggParam);
|
||||
expect(aggParams[0]).to.be.a(BaseAggParam);
|
||||
});
|
||||
|
||||
it('Always converts the params to a BaseAggParam', function () {
|
||||
var aggParams = new AggParams([
|
||||
{
|
||||
name: 'height',
|
||||
editor: '<blink>high</blink>'
|
||||
},
|
||||
{
|
||||
name: 'weight',
|
||||
editor: '<blink>big</blink>'
|
||||
},
|
||||
{
|
||||
name: 'waist',
|
||||
editor: '<blink>small</blink>'
|
||||
}
|
||||
]);
|
||||
|
||||
expect(BaseAggParam).to.have.property('callCount', 3);
|
||||
expect(FieldAggParam).to.have.property('callCount', 0);
|
||||
expect(OptionedAggParam).to.have.property('callCount', 0);
|
||||
|
||||
expect(aggParams).to.have.length(3);
|
||||
aggParams.forEach(function (aggParam) {
|
||||
expect(aggParam).to.be.a(BaseAggParam);
|
||||
});
|
||||
});
|
||||
});
|
||||
}];
|
||||
});
|
100
test/unit/specs/components/agg_types/_agg_type.js
Normal file
100
test/unit/specs/components/agg_types/_agg_type.js
Normal file
|
@ -0,0 +1,100 @@
|
|||
define(function (require) {
|
||||
return ['AggType Class', function () {
|
||||
var _ = require('lodash');
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
var AggType;
|
||||
var AggParams;
|
||||
|
||||
require('services/private');
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
beforeEach(inject(function (Private) {
|
||||
var AggParamsPM = require('components/agg_types/_agg_params');
|
||||
AggParams = sinon.spy(Private(AggParamsPM));
|
||||
Private.stub(AggParamsPM, AggParams);
|
||||
|
||||
AggType = Private(require('components/agg_types/_agg_type'));
|
||||
}));
|
||||
|
||||
describe('constructor', function () {
|
||||
|
||||
it('requires a config object as it\'s first param', function () {
|
||||
expect(function () {
|
||||
new AggType(null);
|
||||
}).to.throwError();
|
||||
});
|
||||
|
||||
describe('application of config properties', function () {
|
||||
var copiedConfigProps = [
|
||||
'name',
|
||||
'title',
|
||||
'makeLabel',
|
||||
'ordered'
|
||||
];
|
||||
|
||||
describe('"' + copiedConfigProps.join('", "') + '"', function () {
|
||||
it('assigns the config value to itself', function () {
|
||||
var config = _.transform(copiedConfigProps, function (config, prop) {
|
||||
config[prop] = {};
|
||||
}, {});
|
||||
|
||||
var aggType = new AggType(config);
|
||||
|
||||
copiedConfigProps.forEach(function (prop) {
|
||||
expect(aggType[prop]).to.be(config[prop]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('makeLabel', function () {
|
||||
it('makes a function when the makeLabel config is not specified', function () {
|
||||
var someGetter = function () {};
|
||||
|
||||
var aggType = new AggType({
|
||||
makeLabel: someGetter
|
||||
});
|
||||
|
||||
expect(aggType.makeLabel).to.be(someGetter);
|
||||
|
||||
aggType = new AggType({
|
||||
name: 'pizza'
|
||||
});
|
||||
|
||||
expect(aggType.makeLabel).to.be.a('function');
|
||||
expect(aggType.makeLabel()).to.be('pizza');
|
||||
});
|
||||
});
|
||||
|
||||
describe('params', function () {
|
||||
it('defaults to an empty AggParams object', function () {
|
||||
var aggType = new AggType({
|
||||
name: 'smart agg'
|
||||
});
|
||||
|
||||
expect(aggType.params).to.be.an(AggParams);
|
||||
expect(aggType.params.length).to.be(0);
|
||||
});
|
||||
|
||||
it('passes the params arg directly to the AggParams constructor', function () {
|
||||
var params = [
|
||||
{name: 'one'},
|
||||
{name: 'two'}
|
||||
];
|
||||
|
||||
var aggType = new AggType({
|
||||
name: 'bucketeer',
|
||||
params: params
|
||||
});
|
||||
|
||||
expect(aggType.params).to.be.an(AggParams);
|
||||
expect(aggType.params.length).to.be(2);
|
||||
expect(AggParams.callCount).to.be(1);
|
||||
expect(AggParams.firstCall.args[0]).to.be(params);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}];
|
||||
});
|
5
test/unit/specs/components/agg_types/_metric_aggs.js
Normal file
5
test/unit/specs/components/agg_types/_metric_aggs.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
define(function (require) {
|
||||
return ['AggParams', function () {
|
||||
|
||||
}];
|
||||
});
|
|
@ -0,0 +1,113 @@
|
|||
define(function (require) {
|
||||
return ['Date Histogram Agg', function () {
|
||||
var _ = require('lodash');
|
||||
|
||||
describe('ordered', function () {
|
||||
var histogram;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
beforeEach(inject(function (Private) {
|
||||
histogram = Private(require('components/agg_types/index')).byName.histogram;
|
||||
}));
|
||||
|
||||
it('is ordered', function () {
|
||||
expect(histogram.ordered).to.be.ok();
|
||||
});
|
||||
|
||||
it('is not ordered by date', function () {
|
||||
expect(histogram.ordered).to.not.have.property('date');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('params', function () {
|
||||
var paramWriter;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
beforeEach(inject(function (Private) {
|
||||
var AggParamWriter = Private(require('test_utils/agg_param_writer'));
|
||||
paramWriter = new AggParamWriter({ aggType: 'histogram' });
|
||||
}));
|
||||
|
||||
describe('interval', function () {
|
||||
// reads aggConfig.params.interval, writes to DSL.interval
|
||||
|
||||
it('accepts a number', function () {
|
||||
var output = paramWriter.write({ interval: 100 });
|
||||
expect(output.params).to.have.property('interval', 100);
|
||||
});
|
||||
|
||||
it('accepts a string', function () {
|
||||
var output = paramWriter.write({ interval: '10' });
|
||||
expect(output.params).to.have.property('interval', 10);
|
||||
});
|
||||
|
||||
it('fails on non-numeric values', function () {
|
||||
// template validation prevents this from users, not devs
|
||||
var output = paramWriter.write({ interval: [] });
|
||||
expect(isNaN(output.params.interval)).to.be.ok();
|
||||
});
|
||||
});
|
||||
|
||||
describe('min_doc_count', function () {
|
||||
it('casts true values to 0', function () {
|
||||
var output = paramWriter.write({ min_doc_count: true });
|
||||
expect(output.params).to.have.property('min_doc_count', 0);
|
||||
|
||||
output = paramWriter.write({ min_doc_count: 'yes' });
|
||||
expect(output.params).to.have.property('min_doc_count', 0);
|
||||
|
||||
output = paramWriter.write({ min_doc_count: 1 });
|
||||
expect(output.params).to.have.property('min_doc_count', 0);
|
||||
|
||||
output = paramWriter.write({ min_doc_count: {} });
|
||||
expect(output.params).to.have.property('min_doc_count', 0);
|
||||
});
|
||||
|
||||
it('writes nothing for false values', function () {
|
||||
var output = paramWriter.write({ min_doc_count: '' });
|
||||
expect(output.params).to.not.have.property('min_doc_count');
|
||||
|
||||
output = paramWriter.write({ min_doc_count: null });
|
||||
expect(output.params).to.not.have.property('min_doc_count');
|
||||
|
||||
output = paramWriter.write({ min_doc_count: undefined });
|
||||
expect(output.params).to.not.have.property('min_doc_count');
|
||||
});
|
||||
});
|
||||
|
||||
describe('extended_bounds', function () {
|
||||
it('writes when only eb.min is set', function () {
|
||||
var output = paramWriter.write({
|
||||
extended_bounds: { min: 0 }
|
||||
});
|
||||
expect(output.params.extended_bounds).to.have.property('min', 0);
|
||||
expect(output.params.extended_bounds).to.have.property('max', undefined);
|
||||
});
|
||||
|
||||
it('writes when only eb.max is set', function () {
|
||||
var output = paramWriter.write({
|
||||
extended_bounds: { max: 0 }
|
||||
});
|
||||
expect(output.params.extended_bounds).to.have.property('min', undefined);
|
||||
expect(output.params.extended_bounds).to.have.property('max', 0);
|
||||
});
|
||||
|
||||
it('writes when both eb.min and eb.max are set', function () {
|
||||
var output = paramWriter.write({
|
||||
extended_bounds: { min: 99, max: 100 }
|
||||
});
|
||||
expect(output.params.extended_bounds).to.have.property('min', 99);
|
||||
expect(output.params.extended_bounds).to.have.property('max', 100);
|
||||
});
|
||||
|
||||
it('does not write when nothing is set', function () {
|
||||
var output = paramWriter.write({
|
||||
extended_bounds: {}
|
||||
});
|
||||
expect(output.params).to.not.have.property('extended_bounds');
|
||||
});
|
||||
});
|
||||
});
|
||||
}];
|
||||
});
|
113
test/unit/specs/components/agg_types/bucket_aggs/histogram.js
Normal file
113
test/unit/specs/components/agg_types/bucket_aggs/histogram.js
Normal file
|
@ -0,0 +1,113 @@
|
|||
define(function (require) {
|
||||
return ['Histogram Agg', function () {
|
||||
var _ = require('lodash');
|
||||
|
||||
describe('ordered', function () {
|
||||
var histogram;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
beforeEach(inject(function (Private) {
|
||||
histogram = Private(require('components/agg_types/index')).byName.histogram;
|
||||
}));
|
||||
|
||||
it('is ordered', function () {
|
||||
expect(histogram.ordered).to.be.ok();
|
||||
});
|
||||
|
||||
it('is not ordered by date', function () {
|
||||
expect(histogram.ordered).to.not.have.property('date');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('params', function () {
|
||||
var paramWriter;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
beforeEach(inject(function (Private) {
|
||||
var AggParamWriter = Private(require('test_utils/agg_param_writer'));
|
||||
paramWriter = new AggParamWriter({ aggType: 'histogram' });
|
||||
}));
|
||||
|
||||
describe('interval', function () {
|
||||
// reads aggConfig.params.interval, writes to DSL.interval
|
||||
|
||||
it('accepts a number', function () {
|
||||
var output = paramWriter.write({ interval: 100 });
|
||||
expect(output.params).to.have.property('interval', 100);
|
||||
});
|
||||
|
||||
it('accepts a string', function () {
|
||||
var output = paramWriter.write({ interval: '10' });
|
||||
expect(output.params).to.have.property('interval', 10);
|
||||
});
|
||||
|
||||
it('fails on non-numeric values', function () {
|
||||
// template validation prevents this from users, not devs
|
||||
var output = paramWriter.write({ interval: [] });
|
||||
expect(isNaN(output.params.interval)).to.be.ok();
|
||||
});
|
||||
});
|
||||
|
||||
describe('min_doc_count', function () {
|
||||
it('casts true values to 0', function () {
|
||||
var output = paramWriter.write({ min_doc_count: true });
|
||||
expect(output.params).to.have.property('min_doc_count', 0);
|
||||
|
||||
output = paramWriter.write({ min_doc_count: 'yes' });
|
||||
expect(output.params).to.have.property('min_doc_count', 0);
|
||||
|
||||
output = paramWriter.write({ min_doc_count: 1 });
|
||||
expect(output.params).to.have.property('min_doc_count', 0);
|
||||
|
||||
output = paramWriter.write({ min_doc_count: {} });
|
||||
expect(output.params).to.have.property('min_doc_count', 0);
|
||||
});
|
||||
|
||||
it('writes nothing for false values', function () {
|
||||
var output = paramWriter.write({ min_doc_count: '' });
|
||||
expect(output.params).to.not.have.property('min_doc_count');
|
||||
|
||||
output = paramWriter.write({ min_doc_count: null });
|
||||
expect(output.params).to.not.have.property('min_doc_count');
|
||||
|
||||
output = paramWriter.write({ min_doc_count: undefined });
|
||||
expect(output.params).to.not.have.property('min_doc_count');
|
||||
});
|
||||
});
|
||||
|
||||
describe('extended_bounds', function () {
|
||||
it('writes when only eb.min is set', function () {
|
||||
var output = paramWriter.write({
|
||||
extended_bounds: { min: 0 }
|
||||
});
|
||||
expect(output.params.extended_bounds).to.have.property('min', 0);
|
||||
expect(output.params.extended_bounds).to.have.property('max', undefined);
|
||||
});
|
||||
|
||||
it('writes when only eb.max is set', function () {
|
||||
var output = paramWriter.write({
|
||||
extended_bounds: { max: 0 }
|
||||
});
|
||||
expect(output.params.extended_bounds).to.have.property('min', undefined);
|
||||
expect(output.params.extended_bounds).to.have.property('max', 0);
|
||||
});
|
||||
|
||||
it('writes when both eb.min and eb.max are set', function () {
|
||||
var output = paramWriter.write({
|
||||
extended_bounds: { min: 99, max: 100 }
|
||||
});
|
||||
expect(output.params.extended_bounds).to.have.property('min', 99);
|
||||
expect(output.params.extended_bounds).to.have.property('max', 100);
|
||||
});
|
||||
|
||||
it('does not write when nothing is set', function () {
|
||||
var output = paramWriter.write({
|
||||
extended_bounds: {}
|
||||
});
|
||||
expect(output.params).to.not.have.property('extended_bounds');
|
||||
});
|
||||
});
|
||||
});
|
||||
}];
|
||||
});
|
13
test/unit/specs/components/agg_types/index.js
Normal file
13
test/unit/specs/components/agg_types/index.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
define(function (require) {
|
||||
describe('AggTypesComponent', function () {
|
||||
var childSuites = [
|
||||
require('specs/components/agg_types/_agg_type'),
|
||||
require('specs/components/agg_types/_agg_params'),
|
||||
require('specs/components/agg_types/bucket_aggs/histogram'),
|
||||
require('specs/components/agg_types/bucket_aggs/date_histogram'),
|
||||
require('specs/components/agg_types/_metric_aggs')
|
||||
].forEach(function (s) {
|
||||
describe(s[0], s[1]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,45 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
|
||||
function ParamClassStub(parent, body) {
|
||||
var stub = sinon.spy(body || function () {
|
||||
stub.Super && stub.Super.call(this);
|
||||
});
|
||||
if (parent) _.inherits(stub, parent);
|
||||
return stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* stub all of the param classes, but ensure that they still inherit properly.
|
||||
* This method should be passed directly to inject();
|
||||
*
|
||||
* ```js
|
||||
* var stubParamClasses = require('specs/components/agg_types/utils/stub_agg_params');
|
||||
* describe('something', function () {
|
||||
* beforeEach(inject(stubParamClasses));
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @param {PrivateLoader} Private - The private module loader, inject by passing this function to inject()
|
||||
* @return {undefined}
|
||||
*/
|
||||
return function stubParamClasses(Private) {
|
||||
var BaseAggParam = Private.stub(
|
||||
require('components/agg_types/param_types/base'),
|
||||
ParamClassStub(null, function (config) {
|
||||
_.assign(this, config);
|
||||
})
|
||||
);
|
||||
|
||||
Private.stub(
|
||||
require('components/agg_types/param_types/field'),
|
||||
ParamClassStub(BaseAggParam)
|
||||
);
|
||||
|
||||
Private.stub(
|
||||
require('components/agg_types/param_types/optioned'),
|
||||
ParamClassStub(BaseAggParam)
|
||||
);
|
||||
};
|
||||
});
|
100
test/utils/agg_param_writer.js
Normal file
100
test/utils/agg_param_writer.js
Normal file
|
@ -0,0 +1,100 @@
|
|||
define(function (require) {
|
||||
return function AggParamWriterHelper(Private) {
|
||||
var _ = require('lodash');
|
||||
var Vis = Private(require('components/vis/vis'));
|
||||
var aggTypes = Private(require('components/agg_types/index'));
|
||||
var visTypes = Private(require('components/vis_types/index'));
|
||||
var stubbedLogstashIndexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
|
||||
|
||||
/**
|
||||
* Helper object for writing aggParams. Specify an aggType and it will find a vis & schema, and
|
||||
* wire up the supporting objects required to feed in parameters, and get #write() output.
|
||||
*
|
||||
* Use cases:
|
||||
* - Verify that the interval parameter of the histogram visualization casts it's input to a number
|
||||
* ```js
|
||||
* it('casts to a number', function () {
|
||||
* var writer = new AggParamWriter({ aggType: 'histogram' });
|
||||
* var output = writer.write({ interval : '100/10' });
|
||||
* expect(output.params.interval).to.be.a('number');
|
||||
* expect(output.params.interval).to.be(100);
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @class AggParamWriter
|
||||
* @param {object} opts - describe the properties of this paramWriter
|
||||
* @param {string} opts.aggType - the name of the aggType we want to test. ('histogram', 'filter', etc.)
|
||||
*/
|
||||
function AggParamWriter(opts) {
|
||||
var self = this;
|
||||
|
||||
self.aggType = opts.aggType;
|
||||
if (_.isString(self.aggType)) {
|
||||
self.aggType = aggTypes.byName[self.aggType];
|
||||
}
|
||||
|
||||
// not configurable right now, but totally required
|
||||
self.indexPattern = stubbedLogstashIndexPattern;
|
||||
|
||||
// the vis type we will use to write the aggParams
|
||||
self.visType = null;
|
||||
|
||||
// the schema that the aggType satisfies
|
||||
self.visAggSchema = null;
|
||||
|
||||
// find a suitable vis type and schema
|
||||
_.find(visTypes, function (visType) {
|
||||
var schema = _.find(visType.schemas.all, function (schema) {
|
||||
// type, type, type, type, type... :(
|
||||
return schema.group === self.aggType.type;
|
||||
});
|
||||
|
||||
if (schema) {
|
||||
self.visType = visType;
|
||||
self.visAggSchema = schema;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!self.aggType || !self.visType || !self.visAggSchema) {
|
||||
throw new Error('unable to find a usable visType and schema for the ' + opts.aggType + ' agg type');
|
||||
}
|
||||
|
||||
self.vis = new Vis(self.indexPattern, {
|
||||
type: self.visType
|
||||
});
|
||||
}
|
||||
|
||||
AggParamWriter.prototype.write = function (paramValues) {
|
||||
var self = this;
|
||||
paramValues = _.clone(paramValues);
|
||||
|
||||
if (self.aggType.params.byName.field && !paramValues.field) {
|
||||
// pick a field rather than force a field to be specified everywhere
|
||||
if (self.aggType.type === 'metrics') {
|
||||
paramValues.field = _.sample(self.indexPattern.fields.byType.number);
|
||||
} else {
|
||||
paramValues.field = _.sample(self.indexPattern.fields.byType.string);
|
||||
}
|
||||
}
|
||||
|
||||
self.vis.setState({
|
||||
type: self.vis.type.name,
|
||||
aggs: [{
|
||||
type: self.aggType,
|
||||
schema: self.visAggSchema,
|
||||
params: paramValues
|
||||
}]
|
||||
});
|
||||
|
||||
var aggConfig = _.find(self.vis.aggs, function (aggConfig) {
|
||||
return aggConfig.type === self.aggType;
|
||||
});
|
||||
|
||||
return aggConfig.type.params.write(aggConfig);
|
||||
};
|
||||
|
||||
return AggParamWriter;
|
||||
|
||||
};
|
||||
});
|
27
test/utils/stub_index_pattern.js
Normal file
27
test/utils/stub_index_pattern.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
define(function (require) {
|
||||
return function (Private) {
|
||||
var Registry = require('utils/registry/registry');
|
||||
var fieldFormats = Private(require('components/index_patterns/_field_formats'));
|
||||
|
||||
function StubIndexPattern(pattern, timeField, fields) {
|
||||
this.fields = new Registry({
|
||||
index: ['name'],
|
||||
group: ['type'],
|
||||
initialSet: fields.map(function (field) {
|
||||
field.count = field.count || 0;
|
||||
|
||||
// non-enumerable type so that it does not get included in the JSON
|
||||
Object.defineProperty(field, 'format', {
|
||||
enumerable: false,
|
||||
get: function () {
|
||||
fieldFormats.defaultByType[field.type];
|
||||
}
|
||||
});
|
||||
|
||||
return field;
|
||||
})
|
||||
});
|
||||
}
|
||||
return StubIndexPattern;
|
||||
};
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue