diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_param_props.ts b/src/legacy/core_plugins/vis_default_editor/public/components/agg_param_props.ts index 01a41d3c412c2..4f53cd467abd5 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_param_props.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_param_props.ts @@ -19,8 +19,9 @@ import { Field } from 'src/plugins/data/public'; import { VisState } from 'src/legacy/core_plugins/visualizations/public'; -import { AggConfig, AggParam, EditorConfig } from '../legacy_imports'; +import { AggConfig, AggParam } from '../legacy_imports'; import { ComboBoxGroupedOptions } from '../utils'; +import { EditorConfig } from './utils'; // NOTE: we cannot export the interface with export { InterfaceName } // as there is currently a bug on babel typescript transform plugin for it diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params.test.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/agg_params.test.tsx index d782c819c7c41..f271ef63376e1 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params.test.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_params.test.tsx @@ -36,10 +36,8 @@ const mockEditorConfig = { }; jest.mock('ui/new_platform'); -jest.mock('ui/vis/config', () => ({ - editorConfigProviders: { - getConfigForAgg: jest.fn(() => mockEditorConfig), - }, +jest.mock('./utils', () => ({ + getEditorConfig: jest.fn(() => mockEditorConfig), })); jest.mock('./agg_params_helper', () => ({ getAggParamsToRender: jest.fn(() => ({ diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/agg_params.tsx index 47e98f175ab73..5449c9ac07c57 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_params.tsx @@ -23,14 +23,7 @@ import { i18n } from '@kbn/i18n'; import useUnmount from 'react-use/lib/useUnmount'; import { IndexPattern } from 'src/plugins/data/public'; -import { - AggConfig, - AggGroupNames, - editorConfigProviders, - FixedParam, - TimeIntervalParam, - EditorParamConfig, -} from '../legacy_imports'; +import { AggConfig, AggGroupNames } from '../legacy_imports'; import { DefaultEditorAggSelect } from './agg_select'; import { DefaultEditorAggParam } from './agg_param'; @@ -46,6 +39,7 @@ import { initAggParamsState, } from './agg_params_state'; import { DefaultEditorCommonProps } from './agg_common_props'; +import { EditorParamConfig, TimeIntervalParam, FixedParam, getEditorConfig } from './utils'; const FIXED_VALUE_PROP = 'fixedValue'; const DEFAULT_PROP = 'default'; @@ -93,10 +87,12 @@ function DefaultEditorAggParams({ values: { schema: agg.schema.title }, }) : ''; - - const editorConfig = useMemo(() => editorConfigProviders.getConfigForAgg(indexPattern, agg), [ + const aggTypeName = agg.type?.name; + const fieldName = agg.params?.field?.name; + const editorConfig = useMemo(() => getEditorConfig(indexPattern, aggTypeName, fieldName), [ indexPattern, - agg, + aggTypeName, + fieldName, ]); const params = useMemo(() => getAggParamsToRender({ agg, editorConfig, metricAggs, state }), [ agg, diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.test.ts b/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.test.ts index 6f584b4329500..40c76961cb594 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.test.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.test.ts @@ -19,20 +19,14 @@ import { IndexPattern, Field } from 'src/plugins/data/public'; import { VisState } from 'src/legacy/core_plugins/visualizations/public'; -import { - AggConfig, - AggType, - AggGroupNames, - BUCKET_TYPES, - IndexedArray, - EditorConfig, -} from '../legacy_imports'; +import { AggConfig, AggType, AggGroupNames, BUCKET_TYPES, IndexedArray } from '../legacy_imports'; import { getAggParamsToRender, getAggTypeOptions, isInvalidParamsTouched, } from './agg_params_helper'; import { FieldParamEditor, OrderByParamEditor } from './controls'; +import { EditorConfig } from './utils'; jest.mock('../utils', () => ({ groupAndSortBy: jest.fn(() => ['indexedFields']), diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.ts b/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.ts index 21154bd7ad603..4b3b9084a4eef 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.ts @@ -33,8 +33,8 @@ import { AggParam, FieldParamType, AggType, - EditorConfig, } from '../legacy_imports'; +import { EditorConfig } from './utils'; interface ParamInstanceBase { agg: AggConfig; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/test_utils.ts b/src/legacy/core_plugins/vis_default_editor/public/components/controls/test_utils.ts index 4e811f4543412..0e6c30101d065 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/test_utils.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/test_utils.ts @@ -18,7 +18,8 @@ */ import { VisState } from 'src/legacy/core_plugins/visualizations/public'; -import { AggConfig, AggParam, EditorConfig } from '../../legacy_imports'; +import { AggConfig, AggParam } from '../../legacy_imports'; +import { EditorConfig } from '../utils'; export const aggParamCommonPropsMock = { agg: {} as AggConfig, diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/time_interval.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/controls/time_interval.tsx index 6168890c2f2da..5da0d6462a8ba 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/time_interval.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/time_interval.tsx @@ -107,7 +107,7 @@ function TimeIntervalParamEditor({ const onChange = (opts: EuiComboBoxOptionProps[]) => { const selectedOpt: ComboBoxOption = get(opts, '0'); - setValue(selectedOpt ? selectedOpt.key : selectedOpt); + setValue(selectedOpt ? selectedOpt.key : ''); if (selectedOpt) { agg.write(); diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/utils/editor_config.ts b/src/legacy/core_plugins/vis_default_editor/public/components/utils/editor_config.ts new file mode 100644 index 0000000000000..d6e6326afdec1 --- /dev/null +++ b/src/legacy/core_plugins/vis_default_editor/public/components/utils/editor_config.ts @@ -0,0 +1,135 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { IndexPattern } from 'src/plugins/data/public'; + +/** + * A hidden parameter can be hidden from the UI completely. + */ +interface Param { + hidden?: boolean; + help?: string; +} + +/** + * A fixed parameter has a fixed value for a specific field. + * It can optionally also be hidden. + */ +export type FixedParam = Partial & { + fixedValue: any; +}; + +/** + * Numeric interval parameters must always be set in the editor to a multiple of + * the specified base. It can optionally also be hidden. + */ +export type NumericIntervalParam = Partial & { + base: number; +}; + +/** + * Time interval parameters must always be set in the editor to a multiple of + * the specified base. It can optionally also be hidden. + */ +export type TimeIntervalParam = Partial & { + default: string; + timeBase: string; +}; + +export type EditorParamConfig = NumericIntervalParam | TimeIntervalParam | FixedParam | Param; + +export interface EditorConfig { + [paramName: string]: EditorParamConfig; +} + +export function getEditorConfig( + indexPattern: IndexPattern, + aggTypeName: string, + fieldName: string +): EditorConfig { + const aggRestrictions = indexPattern.getAggregationRestrictions(); + + if (!aggRestrictions || !aggTypeName || !fieldName) { + return {}; + } + + // Exclude certain param options for terms: + // otherBucket, missingBucket, orderBy, orderAgg + if (aggTypeName === 'terms') { + return { + otherBucket: { + hidden: true, + }, + missingBucket: { + hidden: true, + }, + }; + } + + const fieldAgg = aggRestrictions[aggTypeName] && aggRestrictions[aggTypeName][fieldName]; + + if (!fieldAgg) { + return {}; + } + + // Set interval and base interval for histograms based on agg restrictions + if (aggTypeName === 'histogram') { + const interval = fieldAgg.interval; + return interval + ? { + intervalBase: { + fixedValue: interval, + }, + interval: { + base: interval, + help: i18n.translate('visDefaultEditor.editorConfig.histogram.interval.helpText', { + defaultMessage: 'Must be a multiple of rollup configuration interval: {interval}', + values: { interval }, + }), + }, + } + : {}; + } + + // Set date histogram time zone based on rollup capabilities + if (aggTypeName === 'date_histogram') { + // Interval is deprecated on date_histogram rollups, but may still be present + // See https://github.com/elastic/kibana/pull/36310 + const interval = fieldAgg.calendar_interval || fieldAgg.fixed_interval; + return { + useNormalizedEsInterval: { + fixedValue: false, + }, + interval: { + default: interval, + timeBase: interval, + help: i18n.translate( + 'visDefaultEditor.editorConfig.dateHistogram.customInterval.helpText', + { + defaultMessage: 'Must be a multiple of configuration interval: {interval}', + values: { interval }, + } + ), + }, + }; + } + + return {}; +} diff --git a/src/legacy/ui/public/vis/config/index.ts b/src/legacy/core_plugins/vis_default_editor/public/components/utils/index.ts similarity index 86% rename from src/legacy/ui/public/vis/config/index.ts rename to src/legacy/core_plugins/vis_default_editor/public/components/utils/index.ts index ee7385518a85d..14570356103b1 100644 --- a/src/legacy/ui/public/vis/config/index.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/components/utils/index.ts @@ -17,5 +17,4 @@ * under the License. */ -export { editorConfigProviders, EditorConfigProviderRegistry } from './editor_config_providers'; -export * from './types'; +export * from './editor_config'; diff --git a/src/legacy/core_plugins/vis_default_editor/public/legacy_imports.ts b/src/legacy/core_plugins/vis_default_editor/public/legacy_imports.ts index 5c617f3dc8681..6d7be59c4ec13 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/legacy_imports.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/legacy_imports.ts @@ -55,4 +55,3 @@ export { getDocLink } from 'ui/documentation_links'; export { documentationLinks } from 'ui/documentation_links/documentation_links'; export { move } from 'ui/utils/collection'; export * from 'ui/vis/lib'; -export * from 'ui/vis/config'; diff --git a/src/legacy/ui/public/vis/config/editor_config_providers.test.ts b/src/legacy/ui/public/vis/config/editor_config_providers.test.ts deleted file mode 100644 index 9d93930c09ebc..0000000000000 --- a/src/legacy/ui/public/vis/config/editor_config_providers.test.ts +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { AggConfig } from 'ui/agg_types'; -import { EditorConfigProviderRegistry } from './editor_config_providers'; -import { EditorParamConfig, FixedParam, NumericIntervalParam, TimeIntervalParam } from './types'; - -jest.mock('ui/new_platform'); - -describe('EditorConfigProvider', () => { - let registry: EditorConfigProviderRegistry; - const indexPattern = { - id: '1234', - title: 'logstash-*', - fields: [ - { - name: 'response', - type: 'number', - esTypes: ['integer'], - aggregatable: true, - filterable: true, - searchable: true, - }, - ], - } as any; - - beforeEach(() => { - registry = new EditorConfigProviderRegistry(); - }); - - it('should call registered providers with given parameters', () => { - const provider = jest.fn(() => ({})); - registry.register(provider); - expect(provider).not.toHaveBeenCalled(); - const aggConfig = {} as AggConfig; - registry.getConfigForAgg(indexPattern, aggConfig); - expect(provider).toHaveBeenCalledWith(indexPattern, aggConfig); - }); - - it('should call all registered providers with given parameters', () => { - const provider = jest.fn(() => ({})); - const provider2 = jest.fn(() => ({})); - registry.register(provider); - registry.register(provider2); - expect(provider).not.toHaveBeenCalled(); - expect(provider2).not.toHaveBeenCalled(); - const aggConfig = {} as AggConfig; - registry.getConfigForAgg(indexPattern, aggConfig); - expect(provider).toHaveBeenCalledWith(indexPattern, aggConfig); - expect(provider2).toHaveBeenCalledWith(indexPattern, aggConfig); - }); - - describe('merging configs', () => { - function singleConfig(paramConfig: EditorParamConfig) { - return () => ({ singleParam: paramConfig }); - } - - function getOutputConfig(reg: EditorConfigProviderRegistry) { - return reg.getConfigForAgg(indexPattern, {} as AggConfig).singleParam; - } - - it('should have hidden true if at least one config was hidden true', () => { - registry.register(singleConfig({ hidden: false })); - registry.register(singleConfig({ hidden: true })); - registry.register(singleConfig({ hidden: false })); - const config = getOutputConfig(registry); - expect(config.hidden).toBe(true); - }); - - it('should merge the same fixed values', () => { - registry.register(singleConfig({ fixedValue: 'foo' })); - registry.register(singleConfig({ fixedValue: 'foo' })); - const config = getOutputConfig(registry) as FixedParam; - expect(config).toHaveProperty('fixedValue'); - expect(config.fixedValue).toBe('foo'); - }); - - it('should throw having different fixed values', () => { - registry.register(singleConfig({ fixedValue: 'foo' })); - registry.register(singleConfig({ fixedValue: 'bar' })); - expect(() => { - getOutputConfig(registry); - }).toThrowError(); - }); - - it('should allow same base values', () => { - registry.register(singleConfig({ base: 5 })); - registry.register(singleConfig({ base: 5 })); - const config = getOutputConfig(registry) as NumericIntervalParam; - expect(config).toHaveProperty('base'); - expect(config.base).toBe(5); - }); - - it('should merge multiple base values, using least common multiple', () => { - registry.register(singleConfig({ base: 2 })); - registry.register(singleConfig({ base: 5 })); - registry.register(singleConfig({ base: 8 })); - const config = getOutputConfig(registry) as NumericIntervalParam; - expect(config).toHaveProperty('base'); - expect(config.base).toBe(40); - }); - - it('should throw on combining fixedValue with base', () => { - registry.register(singleConfig({ fixedValue: 'foo' })); - registry.register(singleConfig({ base: 5 })); - expect(() => { - getOutputConfig(registry); - }).toThrowError(); - }); - - it('should allow same timeBase values', () => { - registry.register(singleConfig({ timeBase: '2h', default: '2h' })); - registry.register(singleConfig({ timeBase: '2h', default: '2h' })); - const config = getOutputConfig(registry) as TimeIntervalParam; - expect(config).toHaveProperty('timeBase'); - expect(config).toHaveProperty('default'); - expect(config.timeBase).toBe('2h'); - expect(config.default).toBe('2h'); - }); - - it('should merge multiple compatible timeBase values, using least common interval', () => { - registry.register(singleConfig({ timeBase: '2h', default: '2h' })); - registry.register(singleConfig({ timeBase: '3h', default: '3h' })); - registry.register(singleConfig({ timeBase: '4h', default: '4h' })); - const config = getOutputConfig(registry) as TimeIntervalParam; - expect(config).toHaveProperty('timeBase'); - expect(config).toHaveProperty('default'); - expect(config.timeBase).toBe('12h'); - expect(config.default).toBe('12h'); - }); - - it('should throw on combining incompatible timeBase values', () => { - registry.register(singleConfig({ timeBase: '2h', default: '2h' })); - registry.register(singleConfig({ timeBase: '1d', default: '1d' })); - expect(() => { - getOutputConfig(registry); - }).toThrowError(); - }); - - it('should throw on invalid timeBase values', () => { - registry.register(singleConfig({ timeBase: '2w', default: '2w' })); - expect(() => { - getOutputConfig(registry); - }).toThrowError(); - }); - - it('should throw if timeBase and default are different', () => { - registry.register(singleConfig({ timeBase: '1h', default: '2h' })); - expect(() => { - getOutputConfig(registry); - }).toThrowError(); - }); - - it('should merge hidden together with fixedValue', () => { - registry.register(singleConfig({ fixedValue: 'foo', hidden: true })); - registry.register(singleConfig({ fixedValue: 'foo', hidden: false })); - const config = getOutputConfig(registry) as FixedParam; - expect(config).toHaveProperty('fixedValue'); - expect(config).toHaveProperty('hidden'); - expect(config.fixedValue).toBe('foo'); - expect(config.hidden).toBe(true); - }); - - it('should merge hidden together with base', () => { - registry.register(singleConfig({ base: 2, hidden: false })); - registry.register(singleConfig({ base: 13, hidden: false })); - const config = getOutputConfig(registry) as NumericIntervalParam; - expect(config).toHaveProperty('base'); - expect(config).toHaveProperty('hidden'); - expect(config.base).toBe(26); - expect(config.hidden).toBe(false); - }); - - it('should merge hidden together with timeBase', () => { - registry.register(singleConfig({ timeBase: '2h', default: '2h', hidden: false })); - registry.register(singleConfig({ timeBase: '4h', default: '4h', hidden: false })); - const config = getOutputConfig(registry) as TimeIntervalParam; - expect(config).toHaveProperty('timeBase'); - expect(config).toHaveProperty('default'); - expect(config).toHaveProperty('hidden'); - expect(config.timeBase).toBe('4h'); - expect(config.default).toBe('4h'); - expect(config.hidden).toBe(false); - }); - - it('should merge helps together into one string', () => { - registry.register(singleConfig({ help: 'Warning' })); - registry.register(singleConfig({ help: 'Another help' })); - const config = getOutputConfig(registry); - expect(config).toHaveProperty('help'); - expect(config.help).toBe('Warning\n\nAnother help'); - }); - }); -}); diff --git a/src/legacy/ui/public/vis/config/editor_config_providers.ts b/src/legacy/ui/public/vis/config/editor_config_providers.ts deleted file mode 100644 index 1e82a3ca2762e..0000000000000 --- a/src/legacy/ui/public/vis/config/editor_config_providers.ts +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IndexPattern } from 'src/plugins/data/public'; -import { AggConfig } from 'ui/agg_types'; -import { parseEsInterval } from '../../../../core_plugins/data/public'; -import { - TimeIntervalParam, - EditorConfig, - EditorParamConfig, - FixedParam, - NumericIntervalParam, -} from './types'; -import { leastCommonInterval, leastCommonMultiple } from '../lib'; - -type EditorConfigProvider = (indexPattern: IndexPattern, aggConfig: AggConfig) => EditorConfig; - -class EditorConfigProviderRegistry { - private providers: Set = new Set(); - - public register(configProvider: EditorConfigProvider): void { - this.providers.add(configProvider); - } - - public getConfigForAgg(indexPattern: IndexPattern, aggConfig: AggConfig): EditorConfig { - const configs = Array.from(this.providers).map(provider => provider(indexPattern, aggConfig)); - return this.mergeConfigs(configs); - } - - private isTimeBaseParam(config: EditorParamConfig): config is TimeIntervalParam { - return config.hasOwnProperty('default') && config.hasOwnProperty('timeBase'); - } - - private isBaseParam(config: EditorParamConfig): config is NumericIntervalParam { - return config.hasOwnProperty('base'); - } - - private isFixedParam(config: EditorParamConfig): config is FixedParam { - return config.hasOwnProperty('fixedValue'); - } - - private mergeHidden(current: EditorParamConfig, merged: EditorParamConfig): boolean { - return Boolean(current.hidden || merged.hidden); - } - - private mergeHelp(current: EditorParamConfig, merged: EditorParamConfig): string | undefined { - if (!current.help) { - return merged.help; - } - - return merged.help ? `${merged.help}\n\n${current.help}` : current.help; - } - - private mergeFixedAndBase( - current: EditorParamConfig, - merged: EditorParamConfig, - paramName: string - ): { fixedValue: unknown } | { base: number } | {} { - if ( - this.isFixedParam(current) && - this.isFixedParam(merged) && - current.fixedValue !== merged.fixedValue - ) { - // In case multiple configurations provided a fixedValue, these must all be the same. - // If not we'll throw an error. - throw new Error(`Two EditorConfigProviders provided different fixed values for field ${paramName}: - ${merged.fixedValue} !== ${current.fixedValue}`); - } - - if ( - (this.isFixedParam(current) && this.isBaseParam(merged)) || - (this.isBaseParam(current) && this.isFixedParam(merged)) - ) { - // In case one config tries to set a fixed value and another setting a base value, - // we'll throw an error. This could be solved more elegantly, by allowing fixedValues - // that are the multiple of the specific base value, but since there is no use-case for that - // right now, this isn't implemented. - throw new Error(`Tried to provide a fixedValue and a base for param ${paramName}.`); - } - - if (this.isBaseParam(current) && this.isBaseParam(merged)) { - // In case where both had interval values, just use the least common multiple between both interval - return { - base: leastCommonMultiple(current.base, merged.base), - }; - } - - // In this case we haven't had a fixed value of base for that param yet, we use the one specified - // in the current config - if (this.isFixedParam(current)) { - return { - fixedValue: current.fixedValue, - }; - } - if (this.isBaseParam(current)) { - return { - base: current.base, - }; - } - - return {}; - } - - private mergeTimeBase( - current: TimeIntervalParam, - merged: EditorParamConfig, - paramName: string - ): { timeBase: string; default: string } { - if (current.default !== current.timeBase) { - throw new Error(`Tried to provide differing default and timeBase values for ${paramName}.`); - } - - if (this.isTimeBaseParam(merged)) { - // In case both had where interval values, just use the least common multiple between both intervals - const timeBase = leastCommonInterval(current.timeBase, merged.timeBase); - return { - default: timeBase, - timeBase, - }; - } - - // This code is simply here to throw an error in case the `timeBase` is not a valid ES interval - parseEsInterval(current.timeBase); - return { - default: current.timeBase, - timeBase: current.timeBase, - }; - } - - private mergeConfigs(configs: EditorConfig[]): EditorConfig { - return configs.reduce((output, conf) => { - Object.entries(conf).forEach(([paramName, paramConfig]) => { - if (!output[paramName]) { - output[paramName] = {}; - } - - output[paramName] = { - hidden: this.mergeHidden(paramConfig, output[paramName]), - help: this.mergeHelp(paramConfig, output[paramName]), - ...(this.isTimeBaseParam(paramConfig) - ? this.mergeTimeBase(paramConfig, output[paramName], paramName) - : this.mergeFixedAndBase(paramConfig, output[paramName], paramName)), - }; - }); - return output; - }, {}); - } -} - -const editorConfigProviders = new EditorConfigProviderRegistry(); - -export { editorConfigProviders, EditorConfigProviderRegistry }; diff --git a/src/legacy/ui/public/vis/config/types.ts b/src/legacy/ui/public/vis/config/types.ts deleted file mode 100644 index 61c0ced3cd519..0000000000000 --- a/src/legacy/ui/public/vis/config/types.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * A hidden parameter can be hidden from the UI completely. - */ -interface Param { - hidden?: boolean; - help?: string; -} - -/** - * A fixed parameter has a fixed value for a specific field. - * It can optionally also be hidden. - */ -export type FixedParam = Partial & { - fixedValue: any; -}; - -/** - * Numeric interval parameters must always be set in the editor to a multiple of - * the specified base. It can optionally also be hidden. - */ -export type NumericIntervalParam = Partial & { - base: number; -}; - -/** - * Time interval parameters must always be set in the editor to a multiple of - * the specified base. It can optionally also be hidden. - */ -export type TimeIntervalParam = Partial & { - default: string; - timeBase: string; -}; - -export type EditorParamConfig = NumericIntervalParam | TimeIntervalParam | FixedParam | Param; - -export interface EditorConfig { - [paramName: string]: EditorParamConfig; -} diff --git a/src/plugins/data/public/index_patterns/index.ts b/src/plugins/data/public/index_patterns/index.ts index 7444126ee6cae..3d902ebbb7c23 100644 --- a/src/plugins/data/public/index_patterns/index.ts +++ b/src/plugins/data/public/index_patterns/index.ts @@ -47,4 +47,10 @@ export const indexPatterns = { export { Field, FieldList, IFieldList } from './fields'; // TODO: figure out how to replace IndexPatterns in get_inner_angular. -export { IndexPattern, IndexPatterns, IndexPatternsContract } from './index_patterns'; +export { + IndexPattern, + IndexPatterns, + IndexPatternsContract, + TypeMeta, + AggregationRestrictions, +} from './index_patterns'; diff --git a/src/plugins/data/public/index_patterns/index_patterns/index.ts b/src/plugins/data/public/index_patterns/index_patterns/index.ts index 4ca7e053a4492..fca82025cdc66 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/index.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/index.ts @@ -22,3 +22,4 @@ export * from './format_hit'; export * from './index_pattern'; export * from './index_patterns'; export * from './index_patterns_api_client'; +export * from './types'; diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts index 5c09e22b6dbb4..c09c9f4828799 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts @@ -38,6 +38,7 @@ import { formatHitProvider } from './format_hit'; import { flattenHitWrapper } from './flatten_hit'; import { IIndexPatternsApiClient } from './index_patterns_api_client'; import { getNotifications, getFieldFormats } from '../../services'; +import { TypeMeta } from './types'; const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3; const type = 'index-pattern'; @@ -49,7 +50,7 @@ export class IndexPattern implements IIndexPattern { public title: string = ''; public type?: string; public fieldFormatMap: any; - public typeMeta: any; + public typeMeta?: TypeMeta; public fields: IFieldList; public timeFieldName: string | undefined; public formatHit: any; @@ -336,6 +337,10 @@ export class IndexPattern implements IIndexPattern { return this.fields.getByName(name); } + getAggregationRestrictions() { + return this.typeMeta?.aggs; + } + isWildcard() { return _.includes(this.title, '*'); } diff --git a/src/plugins/data/public/index_patterns/index_patterns/types.ts b/src/plugins/data/public/index_patterns/index_patterns/types.ts new file mode 100644 index 0000000000000..70cddcfb641ff --- /dev/null +++ b/src/plugins/data/public/index_patterns/index_patterns/types.ts @@ -0,0 +1,37 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export type AggregationRestrictions = Record< + string, + { + agg?: string; + interval?: number; + fixed_interval?: string; + calendar_interval?: string; + delay?: string; + time_zone?: string; + } +>; + +export interface TypeMeta { + aggs?: Record; + params?: { + [key: string]: any; + }; +} diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts index c9473c1869868..f0692f120f9b5 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts @@ -15,13 +15,12 @@ import { IndexPatternPersistedState, IndexPatternPrivateState, IndexPatternField, - AggregationRestrictions, } from './types'; import { updateLayerIndexPattern } from './state_helpers'; import { DateRange, ExistingFields } from '../../common/types'; import { BASE_API_URL } from '../../common'; import { documentField } from './document_field'; -import { isNestedField, IFieldType } from '../../../../../../src/plugins/data/public'; +import { isNestedField, IFieldType, TypeMeta } from '../../../../../../src/plugins/data/public'; interface SavedIndexPatternAttributes extends SavedObjectAttributes { title: string; @@ -31,12 +30,7 @@ interface SavedIndexPatternAttributes extends SavedObjectAttributes { typeMeta: string; } -interface SavedRestrictionsObject { - aggs: Record; -} - type SetState = StateSetter; -type SavedRestrictionsInfo = SavedRestrictionsObject | undefined; type SavedObjectsClient = Pick; type ErrorHandler = (err: Error) => void; @@ -275,9 +269,7 @@ function fromSavedObject( fields: (JSON.parse(attributes.fields) as IFieldType[]) .filter(field => !isNestedField(field) && (!!field.aggregatable || !!field.scripted)) .concat(documentField) as IndexPatternField[], - typeMeta: attributes.typeMeta - ? (JSON.parse(attributes.typeMeta) as SavedRestrictionsInfo) - : undefined, + typeMeta: attributes.typeMeta ? (JSON.parse(attributes.typeMeta) as TypeMeta) : undefined, fieldFormatMap: attributes.fieldFormatMap ? JSON.parse(attributes.fieldFormatMap) : undefined, }; @@ -286,21 +278,23 @@ function fromSavedObject( return indexPattern; } - const aggs = Object.keys(typeMeta.aggs); - const newFields = [...(indexPattern.fields as IndexPatternField[])]; - newFields.forEach((field, index) => { - const restrictionsObj: IndexPatternField['aggregationRestrictions'] = {}; - aggs.forEach(agg => { - const restriction = typeMeta.aggs[agg] && typeMeta.aggs[agg][field.name]; - if (restriction) { - restrictionsObj[agg] = restriction; + + if (typeMeta.aggs) { + const aggs = Object.keys(typeMeta.aggs); + newFields.forEach((field, index) => { + const restrictionsObj: IndexPatternField['aggregationRestrictions'] = {}; + aggs.forEach(agg => { + const restriction = typeMeta.aggs && typeMeta.aggs[agg] && typeMeta.aggs[agg][field.name]; + if (restriction) { + restrictionsObj[agg] = restriction; + } + }); + if (Object.keys(restrictionsObj).length) { + newFields[index] = { ...field, aggregationRestrictions: restrictionsObj }; } }); - if (Object.keys(restrictionsObj).length) { - newFields[index] = { ...field, aggregationRestrictions: restrictionsObj }; - } - }); + } return { id: indexPattern.id, diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/date_histogram.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/date_histogram.tsx index dbb6278352f09..07e348535ab02 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/date_histogram.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/date_histogram.tsx @@ -27,7 +27,7 @@ import { updateColumnParam } from '../../state_helpers'; import { OperationDefinition } from '.'; import { FieldBasedIndexPatternColumn } from './column_types'; import { autoIntervalFromDateRange } from '../../auto_date'; -import { AggregationRestrictions } from '../../types'; +import { AggregationRestrictions } from '../../../../../../../../src/plugins/data/public'; const autoInterval = 'auto'; const calendarOnlyIntervals = new Set(['w', 'M', 'q', 'y']); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/types.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/types.ts index e556ddda10679..546d148be1e29 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/types.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/types.ts @@ -5,6 +5,7 @@ */ import { IndexPatternColumn } from './operations'; +import { AggregationRestrictions } from '../../../../../../src/plugins/data/public'; export interface IndexPattern { id: string; @@ -20,18 +21,6 @@ export interface IndexPattern { >; } -export type AggregationRestrictions = Record< - string, - { - agg?: string; - interval?: number; - fixed_interval?: string; - calendar_interval?: string; - delay?: string; - time_zone?: string; - } ->; - export interface IndexPatternField { name: string; type: string; diff --git a/x-pack/legacy/plugins/rollup/public/legacy.ts b/x-pack/legacy/plugins/rollup/public/legacy.ts index a2738372ff346..e52b5c3ae30e6 100644 --- a/x-pack/legacy/plugins/rollup/public/legacy.ts +++ b/x-pack/legacy/plugins/rollup/public/legacy.ts @@ -5,7 +5,6 @@ */ import { npSetup, npStart } from 'ui/new_platform'; -import { editorConfigProviders } from 'ui/vis/config'; import { aggTypeFilters } from 'ui/agg_types/filter'; import { aggTypeFieldFilters } from 'ui/agg_types/param_types/filter'; import { addSearchStrategy } from '../../../../../src/plugins/data/public'; @@ -20,7 +19,6 @@ export const setup = plugin.setup(npSetup.core, { __LEGACY: { aggTypeFilters, aggTypeFieldFilters, - editorConfigProviders, addSearchStrategy, addBadgeExtension, addToggleExtension, diff --git a/x-pack/legacy/plugins/rollup/public/legacy_imports.ts b/x-pack/legacy/plugins/rollup/public/legacy_imports.ts index 981f97963591e..d77e3c98b49d9 100644 --- a/x-pack/legacy/plugins/rollup/public/legacy_imports.ts +++ b/x-pack/legacy/plugins/rollup/public/legacy_imports.ts @@ -9,4 +9,3 @@ export { findIllegalCharactersInIndexName, INDEX_ILLEGAL_CHARACTERS_VISIBLE } fr export { AggTypeFilters } from 'ui/agg_types/filter'; export { AggTypeFieldFilters } from 'ui/agg_types/param_types/filter'; -export { EditorConfigProviderRegistry } from 'ui/vis/config'; diff --git a/x-pack/legacy/plugins/rollup/public/plugin.ts b/x-pack/legacy/plugins/rollup/public/plugin.ts index 97c03fd1fdfc2..4dae078c90f57 100644 --- a/x-pack/legacy/plugins/rollup/public/plugin.ts +++ b/x-pack/legacy/plugins/rollup/public/plugin.ts @@ -6,11 +6,7 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, CoreStart, Plugin } from 'kibana/public'; -import { - EditorConfigProviderRegistry, - AggTypeFilters, - AggTypeFieldFilters, -} from './legacy_imports'; +import { AggTypeFilters, AggTypeFieldFilters } from './legacy_imports'; import { SearchStrategyProvider } from '../../../../../src/plugins/data/public'; import { ManagementSetup as ManagementSetupLegacy } from '../../../../../src/legacy/core_plugins/management/public/np_ready'; import { rollupBadgeExtension, rollupToggleExtension } from './extend_index_management'; @@ -23,8 +19,6 @@ import { getRollupSearchStrategy } from './search/rollup_search_strategy'; import { initAggTypeFilter } from './visualize/agg_type_filter'; // @ts-ignore import { initAggTypeFieldFilter } from './visualize/agg_type_field_filter'; -// @ts-ignore -import { initEditorConfig } from './visualize/editor_config'; import { CONFIG_ROLLUPS } from '../common'; import { FeatureCatalogueCategory, @@ -42,7 +36,6 @@ export interface RollupPluginSetupDependencies { __LEGACY: { aggTypeFilters: AggTypeFilters; aggTypeFieldFilters: AggTypeFieldFilters; - editorConfigProviders: EditorConfigProviderRegistry; addSearchStrategy: (searchStrategy: SearchStrategyProvider) => void; managementLegacy: ManagementSetupLegacy; addBadgeExtension: (badgeExtension: any) => void; @@ -59,7 +52,6 @@ export class RollupPlugin implements Plugin { __LEGACY: { aggTypeFilters, aggTypeFieldFilters, - editorConfigProviders, addSearchStrategy, managementLegacy, addBadgeExtension, @@ -81,7 +73,6 @@ export class RollupPlugin implements Plugin { addSearchStrategy(getRollupSearchStrategy(core.http.fetch)); initAggTypeFilter(aggTypeFilters); initAggTypeFieldFilter(aggTypeFieldFilters); - initEditorConfig(editorConfigProviders); } if (home) { diff --git a/x-pack/legacy/plugins/rollup/public/visualize/editor_config.js b/x-pack/legacy/plugins/rollup/public/visualize/editor_config.js deleted file mode 100644 index 5c1eb7c8ee3b7..0000000000000 --- a/x-pack/legacy/plugins/rollup/public/visualize/editor_config.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -export function initEditorConfig(editorConfigProviders) { - // Limit agg params based on rollup capabilities - editorConfigProviders.register((indexPattern, aggConfig) => { - if (indexPattern.type !== 'rollup') { - return {}; - } - - const aggTypeName = aggConfig.type && aggConfig.type.name; - - // Exclude certain param options for terms: - // otherBucket, missingBucket, orderBy, orderAgg - if (aggTypeName === 'terms') { - return { - otherBucket: { - hidden: true, - }, - missingBucket: { - hidden: true, - }, - }; - } - - const rollupAggs = indexPattern.typeMeta && indexPattern.typeMeta.aggs; - const field = aggConfig.params && aggConfig.params.field && aggConfig.params.field.name; - const fieldAgg = - rollupAggs && field && rollupAggs[aggTypeName] && rollupAggs[aggTypeName][field]; - - if (!rollupAggs || !field || !fieldAgg) { - return {}; - } - - // Set interval and base interval for histograms based on rollup capabilities - if (aggTypeName === 'histogram') { - const interval = fieldAgg.interval; - return interval - ? { - intervalBase: { - fixedValue: interval, - }, - interval: { - base: interval, - help: i18n.translate('xpack.rollupJobs.editorConfig.histogram.interval.helpText', { - defaultMessage: 'Must be a multiple of rollup configuration interval: {interval}', - values: { interval }, - }), - }, - } - : {}; - } - - // Set date histogram time zone based on rollup capabilities - if (aggTypeName === 'date_histogram') { - // Interval is deprecated on date_histogram rollups, but may still be present - // See https://github.com/elastic/kibana/pull/36310 - const interval = fieldAgg.calendar_interval || fieldAgg.fixed_interval || fieldAgg.interval; - return { - useNormalizedEsInterval: { - fixedValue: false, - }, - interval: { - default: interval, - timeBase: interval, - help: i18n.translate( - 'xpack.rollupJobs.editorConfig.dateHistogram.customInterval.helpText', - { - defaultMessage: 'Must be a multiple of rollup configuration interval: {interval}', - values: { interval }, - } - ), - }, - }; - } - - return {}; - }); -}