mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Backport PR #8734
--------- **Commit 1:** [aggConfig] unify field option handling * Original sha:e49ba04d06
* Authored by spalger <email@spalger.com> on 2016-10-18T19:45:24Z **Commit 2:** [tests] fix tests that were improperly using fields * Original sha:a9c139aa13
* Authored by spalger <email@spalger.com> on 2016-10-18T19:56:22Z **Commit 3:** [logstash_fields] remove sortable and filterable, which do nothing * Original sha:0757c45dfa
* Authored by spalger <email@spalger.com> on 2016-10-18T22:11:23Z **Commit 4:** [notifier] ensure that notifications are not left around * Original sha:2dbb462bc1
* Authored by spalger <email@spalger.com> on 2016-10-18T22:48:01Z **Commit 5:** [aggParams/field] support aggParamType#scriptable * Original sha:95d704c37c
* Authored by spalger <email@spalger.com> on 2016-10-17T22:40:04Z **Commit 6:** [aggConfig] add light validation for aggConfig field param * Original sha:0e11c2257b
* Authored by spalger <email@spalger.com> on 2016-10-18T22:47:27Z **Commit 7:** [aggConfig] restore use of `@timestamp` * Original sha:e71b0f26c7
* Authored by spalger <email@spalger.com> on 2016-10-25T00:04:13Z **Commit 8:** [tests] revert changes to notifier clearing * Original sha:6157275f82
* Authored by spalger <email@spalger.com> on 2016-10-25T00:07:31Z **Commit 9:** [aggType/test] remove unnecessary change * Original sha:fbf884a01a
* Authored by spalger <email@spalger.com> on 2016-10-25T00:20:55Z **Commit 10:** [aggTypes/fieldParam] move orderby back into condition * Original sha:001878663d
* Authored by spalger <email@spalger.com> on 2016-10-25T00:22:46Z **Commit 11:** [aggParams/field] use a more descriptive warning * Original sha:7205993507
* Authored by spalger <email@spalger.com> on 2016-10-27T18:38:36Z
This commit is contained in:
parent
f9320886f2
commit
01ca5dd8e5
10 changed files with 130 additions and 87 deletions
|
@ -47,7 +47,8 @@ describe('Vis-Editor-Agg plugin directive', function () {
|
|||
$parentScope.agg = {
|
||||
id: 1,
|
||||
params: {},
|
||||
schema: makeConfig()
|
||||
schema: makeConfig(),
|
||||
getFieldOptions: () => null
|
||||
};
|
||||
$parentScope.groupName = 'metrics';
|
||||
$parentScope.group = [{
|
||||
|
|
|
@ -59,6 +59,7 @@ uiModules
|
|||
|
||||
// create child scope, used in the editors
|
||||
$aggParamEditorsScope = $scope.$new();
|
||||
$aggParamEditorsScope.indexedFields = $scope.agg.getFieldOptions();
|
||||
|
||||
const agg = $scope.agg;
|
||||
if (!agg) return;
|
||||
|
@ -81,10 +82,6 @@ uiModules
|
|||
// build collection of agg params html
|
||||
type.params.forEach(function (param, i) {
|
||||
let aggParam;
|
||||
// if field param exists, compute allowed fields
|
||||
if (param.name === 'field') {
|
||||
$aggParamEditorsScope.indexedFields = getIndexedFields(param);
|
||||
}
|
||||
|
||||
if ($aggParamEditorsScope.indexedFields) {
|
||||
const hasIndexedFields = $aggParamEditorsScope.indexedFields.length > 0;
|
||||
|
@ -136,30 +133,6 @@ uiModules
|
|||
.append(param.editor)
|
||||
.get(0);
|
||||
}
|
||||
|
||||
function getIndexedFields(param) {
|
||||
let fields = _.filter($scope.agg.vis.indexPattern.fields.raw, 'aggregatable');
|
||||
const fieldTypes = param.filterFieldTypes;
|
||||
|
||||
if (fieldTypes) {
|
||||
fields = $filter('fieldType')(fields, fieldTypes);
|
||||
fields = $filter('orderBy')(fields, ['type', 'name']);
|
||||
}
|
||||
|
||||
return new IndexedArray({
|
||||
|
||||
/**
|
||||
* @type {Array}
|
||||
*/
|
||||
index: ['name'],
|
||||
|
||||
/**
|
||||
* [group description]
|
||||
* @type {Array}
|
||||
*/
|
||||
initialSet: fields
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -1,36 +1,65 @@
|
|||
function stubbedLogstashFields() {
|
||||
let sourceData = [
|
||||
{ name: 'bytes', type: 'number', indexed: true, analyzed: true, sortable: true, filterable: true, count: 10 },
|
||||
{ name: 'ssl', type: 'boolean', indexed: true, analyzed: true, sortable: true, filterable: true, count: 20 },
|
||||
{ name: '@timestamp', type: 'date', indexed: true, analyzed: true, sortable: true, filterable: true, count: 30 },
|
||||
{ name: 'time', type: 'date', indexed: true, analyzed: true, sortable: true, filterable: true, count: 30 },
|
||||
{ name: '@tags', type: 'string', indexed: true, analyzed: true, sortable: true, filterable: true },
|
||||
{ name: 'utc_time', type: 'date', indexed: true, analyzed: true, sortable: true, filterable: true },
|
||||
{ name: 'phpmemory', type: 'number', indexed: true, analyzed: true, sortable: true, filterable: true },
|
||||
{ name: 'ip', type: 'ip', indexed: true, analyzed: true, sortable: true, filterable: true },
|
||||
{ name: 'request_body', type: 'attachment', indexed: true, analyzed: true, sortable: false, filterable: true },
|
||||
{ name: 'point', type: 'geo_point', indexed: true, analyzed: true, sortable: false, filterable: false },
|
||||
{ name: 'area', type: 'geo_shape', indexed: true, analyzed: true, sortable: true, filterable: false },
|
||||
{ name: 'hashed', type: 'murmur3', indexed: true, analyzed: true, sortable: false, filterable: false },
|
||||
{ name: 'geo.coordinates', type: 'geo_point', indexed: true, analyzed: true, sortable: false, filterable: true },
|
||||
{ name: 'extension', type: 'string', indexed: true, analyzed: true, sortable: true, filterable: true },
|
||||
{ name: 'machine.os', type: 'string', indexed: true, analyzed: true, sortable: true, filterable: true },
|
||||
{ name: 'geo.src', type: 'string', indexed: true, analyzed: true, sortable: true, filterable: true },
|
||||
{ name: '_type', type: 'string', indexed: false, analyzed: true, sortable: true, filterable: true },
|
||||
{ name: '_id', type: 'string', indexed: false, analyzed: false, sortable: false, filterable: true},
|
||||
{ name: '_source', type: 'string', indexed: false, analyzed: false, sortable: false, filterable: false},
|
||||
{ name: 'custom_user_field', type: 'conflict', indexed: false, analyzed: false, sortable: false, filterable: true },
|
||||
{ name: 'script string', type: 'string', scripted: true, script: '\'i am a string\'', lang: 'expression' },
|
||||
{ name: 'script number', type: 'number', scripted: true, script: '1234', lang: 'expression' },
|
||||
{ name: 'script date', type: 'date', scripted: true, script: '1234', lang: 'painless' },
|
||||
{ name: 'script murmur3', type: 'murmur3', scripted: true, script: '1234', lang: 'expression'},
|
||||
].map(function (field) {
|
||||
field.count = field.count || 0;
|
||||
field.scripted = field.scripted || false;
|
||||
return field;
|
||||
});
|
||||
return [
|
||||
// |indexed
|
||||
// | |analyzed
|
||||
// | | |aggregatable
|
||||
// | | | |searchable
|
||||
// name type | | | | |metadata
|
||||
['bytes', 'number', true, true, true, true, { count: 10 } ],
|
||||
['ssl', 'boolean', true, true, true, true, { count: 20 } ],
|
||||
['@timestamp', 'date', true, true, true, true, { count: 30 } ],
|
||||
['time', 'date', true, true, true, true, { count: 30 } ],
|
||||
['@tags', 'string', true, true, true, true ],
|
||||
['utc_time', 'date', true, true, true, true ],
|
||||
['phpmemory', 'number', true, true, true, true ],
|
||||
['ip', 'ip', true, true, true, true ],
|
||||
['request_body', 'attachment', true, true, true, true ],
|
||||
['point', 'geo_point', true, true, true, true ],
|
||||
['area', 'geo_shape', true, true, true, true ],
|
||||
['hashed', 'murmur3', true, true, false, true ],
|
||||
['geo.coordinates', 'geo_point', true, true, true, true ],
|
||||
['extension', 'string', true, true, true, true ],
|
||||
['machine.os', 'string', true, true, true, true ],
|
||||
['geo.src', 'string', true, true, true, true ],
|
||||
['_id', 'string', false, false, true, true ],
|
||||
['_type', 'string', false, false, true, true ],
|
||||
['_source', 'string', false, false, true, true ],
|
||||
['custom_user_field', 'conflict', false, false, true, true ],
|
||||
['script string', 'string', false, false, true, false, { script: '\'i am a string\'' } ],
|
||||
['script number', 'number', false, false, true, false, { script: '1234' } ],
|
||||
['script date', 'date', false, false, true, false, { script: '1234', lang: 'painless' } ],
|
||||
['script murmur3', 'murmur3', false, false, true, false, { script: '1234' } ],
|
||||
].map(function (row) {
|
||||
const [
|
||||
name,
|
||||
type,
|
||||
indexed,
|
||||
analyzed,
|
||||
aggregatable,
|
||||
searchable,
|
||||
metadata = {}
|
||||
] = row;
|
||||
|
||||
return sourceData;
|
||||
const {
|
||||
count = 0,
|
||||
script,
|
||||
lang = script ? 'expression' : undefined,
|
||||
scripted = !!script,
|
||||
} = metadata;
|
||||
|
||||
return {
|
||||
name,
|
||||
type,
|
||||
indexed,
|
||||
analyzed,
|
||||
aggregatable,
|
||||
searchable,
|
||||
count,
|
||||
script,
|
||||
lang,
|
||||
scripted,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export default stubbedLogstashFields;
|
||||
|
|
|
@ -53,7 +53,7 @@ describe('ResponseWriter class', function () {
|
|||
let aggs = [
|
||||
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } },
|
||||
{ type: 'terms', schema: 'segment', params: { field: 'extension' } },
|
||||
{ type: 'avg', schema: 'metric', params: { field: '@timestamp' } }
|
||||
{ type: 'avg', schema: 'metric', params: { field: 'bytes' } }
|
||||
];
|
||||
|
||||
getColumns.returns(aggs.map(function (agg) {
|
||||
|
|
|
@ -16,6 +16,7 @@ export default function SignificantTermsAggDefinition(Private) {
|
|||
params: [
|
||||
{
|
||||
name: 'field',
|
||||
scriptable: false,
|
||||
filterFieldTypes: 'string'
|
||||
},
|
||||
{
|
||||
|
|
|
@ -43,7 +43,6 @@ export default function TermsAggDefinition(Private) {
|
|||
params: [
|
||||
{
|
||||
name: 'field',
|
||||
scriptable: true,
|
||||
filterFieldTypes: ['number', 'boolean', 'date', 'ip', 'string']
|
||||
},
|
||||
{
|
||||
|
|
|
@ -2,9 +2,13 @@ import { SavedObjectNotFound } from 'ui/errors';
|
|||
import _ from 'lodash';
|
||||
import editorHtml from 'ui/agg_types/controls/field.html';
|
||||
import AggTypesParamTypesBaseProvider from 'ui/agg_types/param_types/base';
|
||||
export default function FieldAggParamFactory(Private) {
|
||||
import 'ui/filters/field_type';
|
||||
import IndexedArray from 'ui/indexed_array';
|
||||
import Notifier from 'ui/notify/notifier';
|
||||
|
||||
export default function FieldAggParamFactory(Private, $filter) {
|
||||
let BaseAggParam = Private(AggTypesParamTypesBaseProvider);
|
||||
const notifier = new Notifier();
|
||||
|
||||
_.class(FieldAggParam).inherits(BaseAggParam);
|
||||
function FieldAggParam(config) {
|
||||
|
@ -12,7 +16,7 @@ export default function FieldAggParamFactory(Private) {
|
|||
}
|
||||
|
||||
FieldAggParam.prototype.editor = editorHtml;
|
||||
FieldAggParam.prototype.scriptable = false;
|
||||
FieldAggParam.prototype.scriptable = true;
|
||||
FieldAggParam.prototype.filterFieldTypes = '*';
|
||||
|
||||
/**
|
||||
|
@ -25,6 +29,32 @@ export default function FieldAggParamFactory(Private) {
|
|||
return field.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the options for this field from the indexPattern
|
||||
*/
|
||||
FieldAggParam.prototype.getFieldOptions = function (aggConfig) {
|
||||
const indexPattern = aggConfig.getIndexPattern();
|
||||
let fields = indexPattern.fields.raw;
|
||||
|
||||
fields = fields.filter(f => f.aggregatable);
|
||||
|
||||
if (!this.scriptable) {
|
||||
fields = fields.filter(field => !field.scripted);
|
||||
}
|
||||
|
||||
if (this.filterFieldTypes) {
|
||||
fields = $filter('fieldType')(fields, this.filterFieldTypes);
|
||||
fields = $filter('orderBy')(fields, ['type', 'name']);
|
||||
}
|
||||
|
||||
|
||||
return new IndexedArray({
|
||||
index: ['name'],
|
||||
group: ['type'],
|
||||
initialSet: fields
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Called to read values from a database record into the
|
||||
* aggConfig object
|
||||
|
@ -33,13 +63,18 @@ export default function FieldAggParamFactory(Private) {
|
|||
* @return {field}
|
||||
*/
|
||||
FieldAggParam.prototype.deserialize = function (fieldName, aggConfig) {
|
||||
let field = aggConfig.vis.indexPattern.fields.byName[fieldName];
|
||||
const field = aggConfig.getIndexPattern().fields.byName[fieldName];
|
||||
|
||||
if (!field) {
|
||||
throw new SavedObjectNotFound('index-pattern-field', fieldName);
|
||||
}
|
||||
|
||||
return field;
|
||||
const validField = this.getFieldOptions(aggConfig).byName[fieldName];
|
||||
if (!validField) {
|
||||
notifier.error(`Saved "field" parameter is now invalid. Please select a new field.`);
|
||||
}
|
||||
|
||||
return validField;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -56,7 +91,7 @@ export default function FieldAggParamFactory(Private) {
|
|||
let field = aggConfig.getField();
|
||||
|
||||
if (!field) {
|
||||
throw new Error(`"${aggConfig.makeLabel()}" requires a field`);
|
||||
throw new TypeError('"field" is a required parameter');
|
||||
}
|
||||
|
||||
if (field.scripted) {
|
||||
|
|
|
@ -12,8 +12,11 @@ import IndexPatternsMapperProvider from 'ui/index_patterns/_mapper';
|
|||
import UtilsMappingSetupProvider from 'ui/utils/mapping_setup';
|
||||
import IndexPatternsIntervalsProvider from 'ui/index_patterns/_intervals';
|
||||
import IndexPatternsIndexPatternProvider from 'ui/index_patterns/_index_pattern';
|
||||
import NoDigestPromises from 'test_utils/no_digest_promises';
|
||||
|
||||
describe('index pattern', function () {
|
||||
NoDigestPromises.activateForSuite();
|
||||
|
||||
let IndexPattern;
|
||||
let mapper;
|
||||
let mappingSetup;
|
||||
|
@ -55,7 +58,7 @@ describe('index pattern', function () {
|
|||
|
||||
// stub calculateIndices
|
||||
calculateIndices = sinon.spy(function () {
|
||||
return $injector.get('Promise').resolve([
|
||||
return Promise.resolve([
|
||||
{ index: 'foo', max: Infinity, min: -Infinity },
|
||||
{ index: 'bar', max: Infinity, min: -Infinity }
|
||||
]);
|
||||
|
@ -150,7 +153,6 @@ describe('index pattern', function () {
|
|||
|
||||
describe('refresh fields', function () {
|
||||
// override the default indexPattern, with a truncated field list
|
||||
require('test_utils/no_digest_promises').activateForSuite();
|
||||
const indexPatternId = 'test-pattern';
|
||||
let indexPattern;
|
||||
let fieldLength;
|
||||
|
@ -321,7 +323,6 @@ describe('index pattern', function () {
|
|||
});
|
||||
|
||||
describe('#toDetailedIndexList', function () {
|
||||
require('test_utils/no_digest_promises').activateForSuite();
|
||||
context('when index pattern is an interval', function () {
|
||||
let interval;
|
||||
beforeEach(function () {
|
||||
|
@ -400,7 +401,6 @@ describe('index pattern', function () {
|
|||
|
||||
describe('#toIndexList', function () {
|
||||
context('when index pattern is an interval', function () {
|
||||
require('test_utils/no_digest_promises').activateForSuite();
|
||||
|
||||
let interval;
|
||||
beforeEach(function () {
|
||||
|
@ -431,7 +431,6 @@ describe('index pattern', function () {
|
|||
});
|
||||
|
||||
context('when index pattern is a time-base wildcard', function () {
|
||||
require('test_utils/no_digest_promises').activateForSuite();
|
||||
beforeEach(function () {
|
||||
sinon.stub(indexPattern, 'getInterval').returns(false);
|
||||
sinon.stub(indexPattern, 'hasTimeField').returns(true);
|
||||
|
@ -453,7 +452,6 @@ describe('index pattern', function () {
|
|||
});
|
||||
|
||||
context('when index pattern is a time-base wildcard that is configured not to expand', function () {
|
||||
require('test_utils/no_digest_promises').activateForSuite();
|
||||
beforeEach(function () {
|
||||
sinon.stub(indexPattern, 'getInterval').returns(false);
|
||||
sinon.stub(indexPattern, 'hasTimeField').returns(true);
|
||||
|
@ -472,13 +470,8 @@ describe('index pattern', function () {
|
|||
sinon.stub(indexPattern, 'getInterval').returns(false);
|
||||
});
|
||||
|
||||
it('is fulfilled by id', function () {
|
||||
let indexList;
|
||||
indexPattern.toIndexList().then(function (val) {
|
||||
indexList = val;
|
||||
});
|
||||
$rootScope.$apply();
|
||||
|
||||
it('is fulfilled by id', async function () {
|
||||
let indexList = await indexPattern.toIndexList();
|
||||
expect(indexList).to.equal(indexPattern.id);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -481,12 +481,12 @@ describe('AggConfig', function () {
|
|||
{
|
||||
type: 'avg',
|
||||
schema: 'metric',
|
||||
params: { field: 'ssl' }
|
||||
params: { field: 'bytes' }
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
let field = indexPattern.fields.byName.ssl;
|
||||
let field = indexPattern.fields.byName.bytes;
|
||||
expect(vis.aggs[0].fieldFormatter('html')).to.be(field.format.getConverterFor('html'));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -148,13 +148,11 @@ export default function AggConfigFactory(Private, fieldTypeFilter) {
|
|||
* @return {object} the new params object
|
||||
*/
|
||||
AggConfig.prototype.resetParams = function () {
|
||||
let fieldParam = this.type && this.type.params.byName.field;
|
||||
let field;
|
||||
const fieldOptions = this.getFieldOptions();
|
||||
|
||||
if (fieldParam) {
|
||||
let prevField = this.params.field;
|
||||
let fieldOpts = fieldTypeFilter(this.vis.indexPattern.fields, fieldParam.filterFieldTypes);
|
||||
field = _.contains(fieldOpts, prevField) ? prevField : null;
|
||||
if (fieldOptions) {
|
||||
field = fieldOptions.byName[this.fieldName()] || null;
|
||||
}
|
||||
|
||||
return this.fillDefaults({ row: this.params.row, field: field });
|
||||
|
@ -286,6 +284,20 @@ export default function AggConfigFactory(Private, fieldTypeFilter) {
|
|||
return pre += this.type.makeLabel(this);
|
||||
};
|
||||
|
||||
AggConfig.prototype.getIndexPattern = function () {
|
||||
return this.vis.indexPattern;
|
||||
};
|
||||
|
||||
AggConfig.prototype.getFieldOptions = function () {
|
||||
const fieldParamType = this.type && this.type.params.byName.field;
|
||||
|
||||
if (!fieldParamType || !fieldParamType.getFieldOptions) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fieldParamType.getFieldOptions(this);
|
||||
};
|
||||
|
||||
AggConfig.prototype.fieldFormatter = function (contentType, defaultFormat) {
|
||||
let format = this.type && this.type.getFormat(this);
|
||||
if (format) return format.getConverterFor(contentType);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue