---------

**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:
Elastic Jasper 2016-10-27 14:46:52 -04:00
parent f9320886f2
commit 01ca5dd8e5
10 changed files with 130 additions and 87 deletions

View file

@ -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 = [{

View file

@ -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
});
}
}
};
});

View file

@ -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;

View file

@ -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) {

View file

@ -16,6 +16,7 @@ export default function SignificantTermsAggDefinition(Private) {
params: [
{
name: 'field',
scriptable: false,
filterFieldTypes: 'string'
},
{

View file

@ -43,7 +43,6 @@ export default function TermsAggDefinition(Private) {
params: [
{
name: 'field',
scriptable: true,
filterFieldTypes: ['number', 'boolean', 'date', 'ip', 'string']
},
{

View file

@ -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) {

View file

@ -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);
});
});

View file

@ -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'));
});
});

View file

@ -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);