diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5a80a11c5cbea..4cc0c8016f1d0 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,16 +4,15 @@ Summarize your PR. If it involves visual changes include a screenshot or gif. ### Checklist -Use ~~strikethroughs~~ to remove checklist items you don't feel are applicable to this PR. +Delete any items that are not applicable to this PR. -- [ ] This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md) - [ ] [Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios - [ ] This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist) +- [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server) +- [ ] This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process) -- [ ] This includes a feature addition or change that requires a release note and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process) - diff --git a/NOTICE.txt b/NOTICE.txt index e0c5d94eff6b3..69be6db72cff2 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -162,8 +162,6 @@ which is available under a "MIT" license. The files based on this license are: - windows_priv_escalation_via_accessibility_features.json - windows_persistence_via_application_shimming.json - windows_execution_via_trusted_developer_utilities.json -- windows_execution_via_net_com_assemblies.json -- windows_execution_via_connection_manager.json MIT License diff --git a/docs/setup/upgrade.asciidoc b/docs/setup/upgrade.asciidoc index 8c03032bb8ac3..982f1167f3871 100644 --- a/docs/setup/upgrade.asciidoc +++ b/docs/setup/upgrade.asciidoc @@ -50,7 +50,7 @@ instructions. [[upgrade-6x]] === Upgrading from 6.x -The recommended path is to upgrade to 6.7 before upgrading to 7.0. This makes it +The recommended path is to upgrade to 6.8 before upgrading to 7.0. This makes it easier to identify the required changes, and enables you to use the Upgrade Assistant to prepare for your upgrade to 7.0. @@ -59,11 +59,11 @@ dashboards is supported. [float] [[upgrade-67]] -=== Upgrading from 6.7 -To help you prepare for your upgrade to 7.0, 6.7 includes an https://www.elastic.co/guide/en/kibana/6.7/upgrade-assistant.html[Upgrade Assistant] -To access the assistant, go to *Management > 7.0 Upgrade Assistant*. +=== Upgrading from 6.8 +To help you prepare for your upgrade to 7.0, 6.8 includes an https://www.elastic.co/guide/en/kibana/6.8/upgrade-assistant.html[Upgrade Assistant] +To access the assistant, go to *Management > 7.0 Upgrade Assistant*. -After you have addressed any issues that were identified by the Upgrade +After you have addressed any issues that were identified by the Upgrade Assistant, <>. diff --git a/docs/siem/images/detections-ui.png b/docs/siem/images/detections-ui.png new file mode 100644 index 0000000000000..3139ffea0767d Binary files /dev/null and b/docs/siem/images/detections-ui.png differ diff --git a/docs/siem/images/hosts-ui.png b/docs/siem/images/hosts-ui.png index 2569df8f419b8..be9fd29246b51 100644 Binary files a/docs/siem/images/hosts-ui.png and b/docs/siem/images/hosts-ui.png differ diff --git a/docs/siem/images/network-ui.png b/docs/siem/images/network-ui.png index 406ac854cd0a2..de8ce89273a02 100644 Binary files a/docs/siem/images/network-ui.png and b/docs/siem/images/network-ui.png differ diff --git a/docs/siem/images/overview-ui.png b/docs/siem/images/overview-ui.png index a34b2fea061c9..6ac02104d6123 100644 Binary files a/docs/siem/images/overview-ui.png and b/docs/siem/images/overview-ui.png differ diff --git a/docs/siem/siem-ui.asciidoc b/docs/siem/siem-ui.asciidoc index 7294cfbc414f4..f01575a21b9f6 100644 --- a/docs/siem/siem-ui.asciidoc +++ b/docs/siem/siem-ui.asciidoc @@ -33,6 +33,23 @@ investigation. [role="screenshot"] image::siem/images/network-ui.png[] +[float] +[[detections-ui]] +=== Detections + +The Detections feature automatically searches for threats and creates +signals when they are detected. Signal detection rules define the conditions +for creating signals. The SIEM app comes with prebuilt rules that search for +suspicious activity on your network and hosts. Additionally, you can +create your own rules. + +See {siem-guide}/detection-engine-overview.html[Detections] in the SIEM +Guide for information on managing detection rules and signals via the UI +or the Detections API. + +[role="screenshot"] +image::siem/images/detections-ui.png[] + [float] [[timelines-ui]] === Timeline diff --git a/src/legacy/core_plugins/data/public/actions/filters/brush_event.test.js b/src/legacy/core_plugins/data/public/actions/filters/brush_event.test.js index a6fe58503cd02..743f6caee4edd 100644 --- a/src/legacy/core_plugins/data/public/actions/filters/brush_event.test.js +++ b/src/legacy/core_plugins/data/public/actions/filters/brush_event.test.js @@ -21,7 +21,7 @@ import _ from 'lodash'; import moment from 'moment'; import expect from '@kbn/expect'; -jest.mock('../../../../../ui/public/agg_types/agg_configs', () => ({ +jest.mock('../../search/aggs', () => ({ AggConfigs: function AggConfigs() { return { createAggConfig: ({ params }) => ({ diff --git a/src/legacy/core_plugins/data/public/index.ts b/src/legacy/core_plugins/data/public/index.ts index 7fe487667f94e..50120292a627a 100644 --- a/src/legacy/core_plugins/data/public/index.ts +++ b/src/legacy/core_plugins/data/public/index.ts @@ -18,7 +18,7 @@ */ // /// Define plugin function -import { DataPlugin as Plugin, DataStart } from './plugin'; +import { DataPlugin as Plugin } from './plugin'; export function plugin() { return new Plugin(); @@ -27,14 +27,58 @@ export function plugin() { // /// Export types & static code /** @public types */ -export { DataStart }; +export { DataSetup, DataStart } from './plugin'; export { SavedQueryAttributes, SavedQuery, SavedQueryTimeFilter, } from '../../../../plugins/data/public'; +export { + // agg_types + AggParam, + AggParamOption, + DateRangeKey, + IAggConfig, + IAggConfigs, + IAggType, + IFieldParamType, + IMetricAggType, + IpRangeKey, + ISchemas, + OptionedParamEditorProps, + OptionedValueProp, +} from './search/types'; /** @public static code */ export * from '../common'; export { FilterStateManager } from './filter/filter_manager'; -export { getRequestInspectorStats, getResponseInspectorStats } from './search'; +export { + // agg_types TODO need to group these under a namespace or prefix + AggParamType, + AggTypeFilters, // TODO convert to interface + aggTypeFilters, + AggTypeFieldFilters, // TODO convert to interface + AggGroupNames, + aggGroupNamesMap, + BUCKET_TYPES, + CidrMask, + convertDateRangeToString, + convertIPRangeToString, + intervalOptions, // only used in Discover + isDateHistogramBucketAggConfig, + isStringType, + isType, + isValidInterval, + isValidJson, + METRIC_TYPES, + OptionedParamType, + parentPipelineType, + propFilter, + Schema, + Schemas, + siblingPipelineType, + termsAggFilter, + // search_source + getRequestInspectorStats, + getResponseInspectorStats, +} from './search'; diff --git a/src/legacy/core_plugins/data/public/plugin.ts b/src/legacy/core_plugins/data/public/plugin.ts index da35366cdff31..ebc470555d87c 100644 --- a/src/legacy/core_plugins/data/public/plugin.ts +++ b/src/legacy/core_plugins/data/public/plugin.ts @@ -45,6 +45,8 @@ import { } from '../../../../plugins/embeddable/public/lib/triggers'; import { IUiActionsSetup, IUiActionsStart } from '../../../../plugins/ui_actions/public'; +import { SearchSetup, SearchStart, SearchService } from './search/search_service'; + export interface DataPluginSetupDependencies { data: DataPublicPluginSetup; expressions: ExpressionsSetup; @@ -56,12 +58,23 @@ export interface DataPluginStartDependencies { uiActions: IUiActionsStart; } +/** + * Interface for this plugin's returned `setup` contract. + * + * @public + */ +export interface DataSetup { + search: SearchSetup; +} + /** * Interface for this plugin's returned `start` contract. * * @public */ -export interface DataStart {} // eslint-disable-line @typescript-eslint/no-empty-interface +export interface DataStart { + search: SearchStart; +} /** * Data Plugin - public @@ -76,7 +89,10 @@ export interface DataStart {} // eslint-disable-line @typescript-eslint/no-empty */ export class DataPlugin - implements Plugin { + implements + Plugin { + private readonly search = new SearchService(); + public setup(core: CoreSetup, { data, uiActions }: DataPluginSetupDependencies) { setInjectedMetadata(core.injectedMetadata); @@ -89,6 +105,10 @@ export class DataPlugin uiActions.registerAction( valueClickAction(data.query.filterManager, data.query.timefilter.timefilter) ); + + return { + search: this.search.setup(core), + }; } public start(core: CoreStart, { data, uiActions }: DataPluginStartDependencies): DataStart { @@ -102,7 +122,9 @@ export class DataPlugin uiActions.attachAction(SELECT_RANGE_TRIGGER, SELECT_RANGE_ACTION); uiActions.attachAction(VALUE_CLICK_TRIGGER, VALUE_CLICK_ACTION); - return {}; + return { + search: this.search.start(core), + }; } public stop() {} diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/_terms_other_bucket_helper.js b/src/legacy/core_plugins/data/public/search/aggs/__tests__/buckets/_terms_other_bucket_helper.js similarity index 99% rename from src/legacy/ui/public/agg_types/__tests__/buckets/_terms_other_bucket_helper.js rename to src/legacy/core_plugins/data/public/search/aggs/__tests__/buckets/_terms_other_bucket_helper.js index acf932c1fb451..247290731df57 100644 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/_terms_other_bucket_helper.js +++ b/src/legacy/core_plugins/data/public/search/aggs/__tests__/buckets/_terms_other_bucket_helper.js @@ -24,7 +24,7 @@ import { mergeOtherBucketAggResponse, updateMissingBucket, } from '../../buckets/_terms_other_bucket_helper'; -import { Vis } from '../../../../../core_plugins/visualizations/public'; +import { Vis } from '../../../../../../../core_plugins/visualizations/public'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; const visConfigSingleTerm = { diff --git a/src/legacy/ui/public/agg_types/agg_config.ts b/src/legacy/core_plugins/data/public/search/aggs/agg_config.ts similarity index 92% rename from src/legacy/ui/public/agg_types/agg_config.ts rename to src/legacy/core_plugins/data/public/search/aggs/agg_config.ts index 17a8b14b57d02..ba7faf8c34b59 100644 --- a/src/legacy/ui/public/agg_types/agg_config.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/agg_config.ts @@ -27,17 +27,17 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import { npStart } from 'ui/new_platform'; -import { AggType } from './agg_type'; +import { IAggType } from './agg_type'; import { AggGroupNames } from './agg_groups'; import { writeParams } from './agg_params'; -import { AggConfigs } from './agg_configs'; +import { IAggConfigs } from './agg_configs'; import { Schema } from './schemas'; import { ISearchSource, FetchOptions, fieldFormats, KBN_FIELD_TYPES, -} from '../../../../plugins/data/public'; +} from '../../../../../../plugins/data/public'; export interface AggConfigOptions { enabled: boolean; @@ -58,15 +58,20 @@ const unknownSchema: Schema = { defaults: {}, editor: false, group: AggGroupNames.Metrics, + aggSettings: { + top_hits: { + allowStrings: true, + }, + }, }; -const getTypeFromRegistry = (type: string): AggType => { +const getTypeFromRegistry = (type: string): IAggType => { // We need to inline require here, since we're having a cyclic dependency // from somewhere inside agg_types back to AggConfig. - const aggTypes = require('./agg_types').aggTypes; + const aggTypes = require('../aggs').aggTypes; const registeredType = - aggTypes.metrics.find((agg: AggType) => agg.name === type) || - aggTypes.buckets.find((agg: AggType) => agg.name === type); + aggTypes.metrics.find((agg: IAggType) => agg.name === type) || + aggTypes.buckets.find((agg: IAggType) => agg.name === type); if (!registeredType) { throw new Error('unknown type'); @@ -85,6 +90,9 @@ const getSchemaFromRegistry = (schemas: any, schema: string): Schema => { return registeredSchema; }; +// TODO need to make a more explicit interface for this +export type IAggConfig = AggConfig; + export class AggConfig { /** * Ensure that all of the objects in the list have ids, the objects @@ -122,19 +130,19 @@ export class AggConfig { ); } - public aggConfigs: AggConfigs; + public aggConfigs: IAggConfigs; public id: string; public enabled: boolean; public params: any; - public parent?: AggConfigs; + public parent?: IAggConfigs; public brandNew?: boolean; private __schema: Schema; - private __type: AggType; + private __type: IAggType; private __typeDecorations: any; private subAggs: AggConfig[] = []; - constructor(aggConfigs: AggConfigs, opts: AggConfigOptions) { + constructor(aggConfigs: IAggConfigs, opts: AggConfigOptions) { this.aggConfigs = aggConfigs; this.id = String(opts.id || AggConfig.nextId(aggConfigs.aggs as any)); this.enabled = typeof opts.enabled === 'boolean' ? opts.enabled : true; @@ -207,7 +215,7 @@ export class AggConfig { return _.get(this.params, key); } - write(aggs?: AggConfigs) { + write(aggs?: IAggConfigs) { return writeParams(this.type.params, this, aggs); } @@ -262,7 +270,7 @@ export class AggConfig { * @return {void|Object} - if the config has a dsl representation, it is * returned, else undefined is returned */ - toDsl(aggConfigs?: AggConfigs) { + toDsl(aggConfigs?: IAggConfigs) { if (this.type.hasNoDsl) return; const output = this.write(aggConfigs) as any; @@ -360,7 +368,7 @@ export class AggConfig { if (!this.type) return ''; return percentageMode - ? i18n.translate('common.ui.vis.aggConfig.percentageOfLabel', { + ? i18n.translate('data.search.aggs.percentageOfLabel', { defaultMessage: 'Percentage of {label}', values: { label: this.type.makeLabel(this) }, }) @@ -435,7 +443,7 @@ export class AggConfig { if (fieldParam) { // @ts-ignore - availableFields = fieldParam.getAvailableFields(this.getIndexPattern().fields); + availableFields = fieldParam.getAvailableFields(this); } // clear out the previous params except for a few special ones @@ -448,7 +456,7 @@ export class AggConfig { }); } - public setType(type: string | AggType) { + public setType(type: string | IAggType) { this.type = typeof type === 'string' ? getTypeFromRegistry(type) : type; } diff --git a/src/legacy/ui/public/agg_types/agg_configs.ts b/src/legacy/core_plugins/data/public/search/aggs/agg_configs.ts similarity index 98% rename from src/legacy/ui/public/agg_types/agg_configs.ts rename to src/legacy/core_plugins/data/public/search/aggs/agg_configs.ts index 47e2222abe1e8..7e7e4944b00da 100644 --- a/src/legacy/ui/public/agg_types/agg_configs.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/agg_configs.ts @@ -35,7 +35,7 @@ import { ISearchSource, FetchOptions, TimeRange, -} from '../../../../plugins/data/public'; +} from '../../../../../../plugins/data/public'; type Schemas = Record; @@ -55,6 +55,9 @@ function parseParentAggs(dslLvlCursor: any, dsl: any) { } } +// TODO need to make a more explicit interface for this +export type IAggConfigs = AggConfigs; + export class AggConfigs { public indexPattern: IndexPattern; public schemas: any; diff --git a/src/legacy/ui/public/agg_types/agg_groups.ts b/src/legacy/core_plugins/data/public/search/aggs/agg_groups.ts similarity index 87% rename from src/legacy/ui/public/agg_types/agg_groups.ts rename to src/legacy/core_plugins/data/public/search/aggs/agg_groups.ts index d08e875bf213e..d21f5c8968840 100644 --- a/src/legacy/ui/public/agg_types/agg_groups.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/agg_groups.ts @@ -28,10 +28,10 @@ export const AggGroupNames = Object.freeze({ export type AggGroupNames = $Values; export const aggGroupNamesMap = () => ({ - [AggGroupNames.Metrics]: i18n.translate('common.ui.aggTypes.aggGroups.metricsText', { + [AggGroupNames.Metrics]: i18n.translate('data.search.aggs.aggGroups.metricsText', { defaultMessage: 'Metrics', }), - [AggGroupNames.Buckets]: i18n.translate('common.ui.aggTypes.aggGroups.bucketsText', { + [AggGroupNames.Buckets]: i18n.translate('data.search.aggs.aggGroups.bucketsText', { defaultMessage: 'Buckets', }), }); diff --git a/src/legacy/ui/public/agg_types/agg_params.test.ts b/src/legacy/core_plugins/data/public/search/aggs/agg_params.test.ts similarity index 97% rename from src/legacy/ui/public/agg_types/agg_params.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/agg_params.test.ts index 25e62e06d52d7..30ab272537dad 100644 --- a/src/legacy/ui/public/agg_types/agg_params.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/agg_params.test.ts @@ -21,7 +21,7 @@ import { initParams } from './agg_params'; import { BaseParamType } from './param_types/base'; import { FieldParamType } from './param_types/field'; import { OptionedParamType } from './param_types/optioned'; -import { AggParamType } from '../agg_types/param_types/agg'; +import { AggParamType } from '../aggs/param_types/agg'; jest.mock('ui/new_platform'); diff --git a/src/legacy/ui/public/agg_types/agg_params.ts b/src/legacy/core_plugins/data/public/search/aggs/agg_params.ts similarity index 97% rename from src/legacy/ui/public/agg_types/agg_params.ts rename to src/legacy/core_plugins/data/public/search/aggs/agg_params.ts index 262a57f4a5aa3..34727ff4614b9 100644 --- a/src/legacy/ui/public/agg_types/agg_params.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/agg_params.ts @@ -25,7 +25,7 @@ import { JsonParamType } from './param_types/json'; import { BaseParamType } from './param_types/base'; import { AggConfig } from './agg_config'; -import { AggConfigs } from './agg_configs'; +import { IAggConfigs } from './agg_configs'; const paramTypeMap = { field: FieldParamType, @@ -73,7 +73,7 @@ export const writeParams = < >( params: Array> = [], aggConfig: TAggConfig, - aggs?: AggConfigs, + aggs?: IAggConfigs, locals?: Record ) => { const output = { params: {} as Record }; diff --git a/src/legacy/ui/public/agg_types/agg_type.test.ts b/src/legacy/core_plugins/data/public/search/aggs/agg_type.test.ts similarity index 94% rename from src/legacy/ui/public/agg_types/agg_type.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/agg_type.test.ts index 9b34910e81e88..6d4c2d1317f50 100644 --- a/src/legacy/ui/public/agg_types/agg_type.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/agg_type.test.ts @@ -18,7 +18,7 @@ */ import { AggType, AggTypeConfig } from './agg_type'; -import { AggConfig } from './agg_config'; +import { IAggConfig } from './agg_config'; import { npStart } from 'ui/new_platform'; jest.mock('ui/new_platform'); @@ -48,7 +48,7 @@ describe('AggType Class', () => { describe('makeLabel', () => { it('makes a function when the makeLabel config is not specified', () => { const makeLabel = () => 'label'; - const aggConfig = {} as AggConfig; + const aggConfig = {} as IAggConfig; const config: AggTypeConfig = { name: 'name', title: 'title', @@ -64,7 +64,7 @@ describe('AggType Class', () => { describe('getResponseAggs/getRequestAggs', () => { it('copies the value', () => { - const testConfig = (aggConfig: AggConfig) => [aggConfig]; + const testConfig = (aggConfig: IAggConfig) => [aggConfig]; const aggType = new AggType({ name: 'name', @@ -78,7 +78,7 @@ describe('AggType Class', () => { }); it('defaults to noop', () => { - const aggConfig = {} as AggConfig; + const aggConfig = {} as IAggConfig; const aggType = new AggType({ name: 'name', title: 'title', @@ -130,13 +130,13 @@ describe('AggType Class', () => { }); describe('getFormat', function() { - let aggConfig: AggConfig; + let aggConfig: IAggConfig; let field: any; beforeEach(() => { aggConfig = ({ getField: jest.fn(() => field), - } as unknown) as AggConfig; + } as unknown) as IAggConfig; }); it('returns the formatter for the aggConfig', () => { diff --git a/src/legacy/ui/public/agg_types/agg_type.ts b/src/legacy/core_plugins/data/public/search/aggs/agg_type.ts similarity index 94% rename from src/legacy/ui/public/agg_types/agg_type.ts rename to src/legacy/core_plugins/data/public/search/aggs/agg_type.ts index 7ec688277b9c4..56299839d0a6d 100644 --- a/src/legacy/ui/public/agg_types/agg_type.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/agg_type.ts @@ -23,11 +23,15 @@ import { npStart } from 'ui/new_platform'; import { initParams } from './agg_params'; import { AggConfig } from './agg_config'; -import { AggConfigs } from './agg_configs'; -import { Adapters } from '../../../../plugins/inspector/public'; +import { IAggConfigs } from './agg_configs'; +import { Adapters } from '../../../../../../plugins/inspector/public'; import { BaseParamType } from './param_types/base'; -import { AggParamType } from '../agg_types/param_types/agg'; -import { KBN_FIELD_TYPES, fieldFormats, ISearchSource } from '../../../../plugins/data/public'; +import { AggParamType } from './param_types/agg'; +import { + KBN_FIELD_TYPES, + fieldFormats, + ISearchSource, +} from '../../../../../../plugins/data/public'; export interface AggTypeConfig< TAggConfig extends AggConfig = AggConfig, @@ -48,7 +52,7 @@ export interface AggTypeConfig< decorateAggConfig?: () => any; postFlightRequest?: ( resp: any, - aggConfigs: AggConfigs, + aggConfigs: IAggConfigs, aggConfig: TAggConfig, searchSource: ISearchSource, inspectorAdapters: Adapters, @@ -66,6 +70,9 @@ const getFormat = (agg: AggConfig) => { return field ? field.format : fieldFormatsService.getDefaultInstance(KBN_FIELD_TYPES.STRING); }; +// TODO need to make a more explicit interface for this +export type IAggType = AggType; + export class AggType< TAggConfig extends AggConfig = AggConfig, TParam extends AggParamType = AggParamType @@ -178,7 +185,7 @@ export class AggType< */ postFlightRequest: ( resp: any, - aggConfigs: AggConfigs, + aggConfigs: IAggConfigs, aggConfig: TAggConfig, searchSource: ISearchSource, inspectorAdapters: Adapters, @@ -239,7 +246,7 @@ export class AggType< if (config.customLabels !== false) { params.push({ name: 'customLabel', - displayName: i18n.translate('common.ui.aggTypes.string.customLabel', { + displayName: i18n.translate('data.search.aggs.string.customLabel', { defaultMessage: 'Custom label', }), type: 'string', diff --git a/src/legacy/ui/public/agg_types/agg_types.ts b/src/legacy/core_plugins/data/public/search/aggs/agg_types.ts similarity index 73% rename from src/legacy/ui/public/agg_types/agg_types.ts rename to src/legacy/core_plugins/data/public/search/aggs/agg_types.ts index 1b05f5926ebfc..c16eb06eeb116 100644 --- a/src/legacy/ui/public/agg_types/agg_types.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/agg_types.ts @@ -50,8 +50,6 @@ import { bucketAvgMetricAgg } from './metrics/bucket_avg'; import { bucketMinMetricAgg } from './metrics/bucket_min'; import { bucketMaxMetricAgg } from './metrics/bucket_max'; -export { AggType } from './agg_type'; - export const aggTypes = { metrics: [ countMetricAgg, @@ -90,3 +88,27 @@ export const aggTypes = { geoTileBucketAgg, ], }; + +export { AggType } from './agg_type'; +export { AggConfig } from './agg_config'; +export { AggConfigs } from './agg_configs'; +export { FieldParamType } from './param_types'; +export { aggTypeFieldFilters } from './param_types/filter'; +export { parentPipelineAggHelper } from './metrics/lib/parent_pipeline_agg_helper'; + +// static code +export { AggParamType } from './param_types/agg'; +export { AggGroupNames, aggGroupNamesMap } from './agg_groups'; +export { intervalOptions } from './buckets/_interval_options'; // only used in Discover +export { isDateHistogramBucketAggConfig, setBounds } from './buckets/date_histogram'; +export { termsAggFilter } from './buckets/terms'; +export { isType, isStringType } from './buckets/migrate_include_exclude_format'; +export { CidrMask } from './buckets/lib/cidr_mask'; +export { convertDateRangeToString } from './buckets/date_range'; +export { convertIPRangeToString } from './buckets/ip_range'; +export { aggTypeFilters, propFilter } from './filter'; +export { OptionedParamType } from './param_types/optioned'; +export { isValidJson, isValidInterval } from './utils'; +export { BUCKET_TYPES } from './buckets/bucket_agg_types'; +export { METRIC_TYPES } from './metrics/metric_agg_types'; +export { ISchemas, Schema, Schemas } from './schemas'; diff --git a/src/legacy/ui/public/agg_types/buckets/_bucket_agg_type.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/_bucket_agg_type.ts similarity index 96% rename from src/legacy/ui/public/agg_types/buckets/_bucket_agg_type.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/_bucket_agg_type.ts index 9b7c97a8f11b6..546d054c5af97 100644 --- a/src/legacy/ui/public/agg_types/buckets/_bucket_agg_type.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/_bucket_agg_type.ts @@ -18,7 +18,7 @@ */ import { AggConfig } from '../agg_config'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; import { AggType, AggTypeConfig } from '../agg_type'; import { AggParamType } from '../param_types/agg'; diff --git a/src/legacy/ui/public/agg_types/buckets/_interval_options.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/_interval_options.ts similarity index 66% rename from src/legacy/ui/public/agg_types/buckets/_interval_options.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/_interval_options.ts index 01d0abb7a366c..e196687607d19 100644 --- a/src/legacy/ui/public/agg_types/buckets/_interval_options.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/_interval_options.ts @@ -21,7 +21,7 @@ import { IBucketAggConfig } from './_bucket_agg_type'; export const intervalOptions = [ { - display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.autoDisplayName', { + display: i18n.translate('data.search.aggs.buckets.intervalOptions.autoDisplayName', { defaultMessage: 'Auto', }), val: 'auto', @@ -32,49 +32,49 @@ export const intervalOptions = [ }, }, { - display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.millisecondDisplayName', { + display: i18n.translate('data.search.aggs.buckets.intervalOptions.millisecondDisplayName', { defaultMessage: 'Millisecond', }), val: 'ms', }, { - display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.secondDisplayName', { + display: i18n.translate('data.search.aggs.buckets.intervalOptions.secondDisplayName', { defaultMessage: 'Second', }), val: 's', }, { - display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.minuteDisplayName', { + display: i18n.translate('data.search.aggs.buckets.intervalOptions.minuteDisplayName', { defaultMessage: 'Minute', }), val: 'm', }, { - display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.hourlyDisplayName', { + display: i18n.translate('data.search.aggs.buckets.intervalOptions.hourlyDisplayName', { defaultMessage: 'Hourly', }), val: 'h', }, { - display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.dailyDisplayName', { + display: i18n.translate('data.search.aggs.buckets.intervalOptions.dailyDisplayName', { defaultMessage: 'Daily', }), val: 'd', }, { - display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.weeklyDisplayName', { + display: i18n.translate('data.search.aggs.buckets.intervalOptions.weeklyDisplayName', { defaultMessage: 'Weekly', }), val: 'w', }, { - display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.monthlyDisplayName', { + display: i18n.translate('data.search.aggs.buckets.intervalOptions.monthlyDisplayName', { defaultMessage: 'Monthly', }), val: 'M', }, { - display: i18n.translate('common.ui.aggTypes.buckets.intervalOptions.yearlyDisplayName', { + display: i18n.translate('data.search.aggs.buckets.intervalOptions.yearlyDisplayName', { defaultMessage: 'Yearly', }), val: 'y', diff --git a/src/legacy/ui/public/agg_types/buckets/_terms_other_bucket_helper.js b/src/legacy/core_plugins/data/public/search/aggs/buckets/_terms_other_bucket_helper.js similarity index 99% rename from src/legacy/ui/public/agg_types/buckets/_terms_other_bucket_helper.js rename to src/legacy/core_plugins/data/public/search/aggs/buckets/_terms_other_bucket_helper.js index c8580183756f4..ddab360161744 100644 --- a/src/legacy/ui/public/agg_types/buckets/_terms_other_bucket_helper.js +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/_terms_other_bucket_helper.js @@ -18,7 +18,7 @@ */ import _ from 'lodash'; -import { esFilters, esQuery } from '../../../../../plugins/data/public'; +import { esFilters, esQuery } from '../../../../../../../plugins/data/public'; import { AggGroupNames } from '../agg_groups'; /** diff --git a/src/legacy/ui/public/agg_types/buckets/bucket_agg_types.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/bucket_agg_types.ts similarity index 100% rename from src/legacy/ui/public/agg_types/buckets/bucket_agg_types.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/bucket_agg_types.ts diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.test.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts similarity index 98% rename from src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts index 9426df7d34c29..e212132257ef6 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts @@ -23,7 +23,7 @@ import { intervalOptions } from '../_interval_options'; import { AggConfigs } from '../../agg_configs'; import { IBucketDateHistogramAggConfig } from '../date_histogram'; import { BUCKET_TYPES } from '../bucket_agg_types'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters } from '../../../../../../../../plugins/data/public'; jest.mock('ui/new_platform'); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/date_histogram.ts similarity index 94% rename from src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/date_histogram.ts index f91a92eab1c33..e634b5daf0ac3 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/date_histogram.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/date_histogram.ts @@ -19,7 +19,7 @@ import moment from 'moment'; import { IBucketDateHistogramAggConfig } from '../date_histogram'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters } from '../../../../../../../../plugins/data/public'; export const createFilterDateHistogram = ( agg: IBucketDateHistogramAggConfig, diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/date_range.test.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts similarity index 97% rename from src/legacy/ui/public/agg_types/buckets/create_filter/date_range.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts index 9c2c4f72704f4..e224253a6e314 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/date_range.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts @@ -19,7 +19,7 @@ import moment from 'moment'; import { createFilterDateRange } from './date_range'; -import { fieldFormats } from '../../../../../../plugins/data/public'; +import { fieldFormats } from '../../../../../../../../plugins/data/public'; import { AggConfigs } from '../../agg_configs'; import { BUCKET_TYPES } from '../bucket_agg_types'; import { IBucketAggConfig } from '../_bucket_agg_type'; diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/date_range.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/date_range.ts similarity index 94% rename from src/legacy/ui/public/agg_types/buckets/create_filter/date_range.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/date_range.ts index 01689d954a072..f7f2cfdb7bb61 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/date_range.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/date_range.ts @@ -20,7 +20,7 @@ import moment from 'moment'; import { IBucketAggConfig } from '../_bucket_agg_type'; import { DateRangeKey } from '../date_range'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters } from '../../../../../../../../plugins/data/public'; export const createFilterDateRange = (agg: IBucketAggConfig, { from, to }: DateRangeKey) => { const filter: esFilters.RangeFilterParams = {}; diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/filters.test.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts similarity index 100% rename from src/legacy/ui/public/agg_types/buckets/create_filter/filters.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/filters.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/filters.ts similarity index 94% rename from src/legacy/ui/public/agg_types/buckets/create_filter/filters.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/filters.ts index 6b614514580b6..715f6895374e6 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/filters.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/filters.ts @@ -19,7 +19,7 @@ import { get } from 'lodash'; import { IBucketAggConfig } from '../_bucket_agg_type'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters } from '../../../../../../../../plugins/data/public'; export const createFilterFilters = (aggConfig: IBucketAggConfig, key: string) => { // have the aggConfig write agg dsl params diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.test.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/histogram.test.ts similarity index 97% rename from src/legacy/ui/public/agg_types/buckets/create_filter/histogram.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/histogram.test.ts index ef49636f9e0c1..1a78967261fa6 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/histogram.test.ts @@ -20,7 +20,7 @@ import { createFilterHistogram } from './histogram'; import { AggConfigs } from '../../agg_configs'; import { BUCKET_TYPES } from '../bucket_agg_types'; import { IBucketAggConfig } from '../_bucket_agg_type'; -import { fieldFormats } from '../../../../../../plugins/data/public'; +import { fieldFormats } from '../../../../../../../../plugins/data/public'; jest.mock('ui/new_platform'); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/histogram.ts similarity index 94% rename from src/legacy/ui/public/agg_types/buckets/create_filter/histogram.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/histogram.ts index fc587fa9ecdb6..820f3de5ae9f0 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/histogram.ts @@ -18,7 +18,7 @@ */ import { IBucketAggConfig } from '../_bucket_agg_type'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters } from '../../../../../../../../plugins/data/public'; export const createFilterHistogram = (aggConfig: IBucketAggConfig, key: string) => { const value = parseInt(key, 10); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.test.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts similarity index 97% rename from src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts index a9eca3bbb7a56..e92ba5cb2852a 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts @@ -19,7 +19,7 @@ import { createFilterIpRange } from './ip_range'; import { AggConfigs } from '../../agg_configs'; -import { fieldFormats } from '../../../../../../plugins/data/public'; +import { fieldFormats } from '../../../../../../../../plugins/data/public'; import { BUCKET_TYPES } from '../bucket_agg_types'; import { IBucketAggConfig } from '../_bucket_agg_type'; diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/ip_range.ts similarity index 95% rename from src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/ip_range.ts index a513b8c782739..d78f4579cd713 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/ip_range.ts @@ -20,7 +20,7 @@ import { CidrMask } from '../lib/cidr_mask'; import { IBucketAggConfig } from '../_bucket_agg_type'; import { IpRangeKey } from '../ip_range'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters } from '../../../../../../../../plugins/data/public'; export const createFilterIpRange = (aggConfig: IBucketAggConfig, key: IpRangeKey) => { let range: esFilters.RangeFilterParams; diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/range.test.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/range.test.ts similarity index 97% rename from src/legacy/ui/public/agg_types/buckets/create_filter/range.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/range.test.ts index 720e952c28821..2f74f23721813 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/range.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/range.test.ts @@ -18,7 +18,7 @@ */ import { createFilterRange } from './range'; -import { fieldFormats } from '../../../../../../plugins/data/public'; +import { fieldFormats } from '../../../../../../../../plugins/data/public'; import { AggConfigs } from '../../agg_configs'; import { BUCKET_TYPES } from '../bucket_agg_types'; import { IBucketAggConfig } from '../_bucket_agg_type'; diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/range.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/range.ts similarity index 93% rename from src/legacy/ui/public/agg_types/buckets/create_filter/range.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/range.ts index 929827c6e3fec..125a30a1ab1dd 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/range.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/range.ts @@ -18,7 +18,7 @@ */ import { IBucketAggConfig } from '../_bucket_agg_type'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters } from '../../../../../../../../plugins/data/public'; export const createFilterRange = (aggConfig: IBucketAggConfig, params: any) => { return esFilters.buildRangeFilter( diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/terms.test.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts similarity index 98% rename from src/legacy/ui/public/agg_types/buckets/create_filter/terms.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts index 86c0aa24f529a..d5fd1337f2cb2 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/terms.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts @@ -21,7 +21,7 @@ import { createFilterTerms } from './terms'; import { AggConfigs } from '../../agg_configs'; import { BUCKET_TYPES } from '../bucket_agg_types'; import { IBucketAggConfig } from '../_bucket_agg_type'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters } from '../../../../../../../../plugins/data/public'; jest.mock('ui/new_platform'); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/terms.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/terms.ts similarity index 95% rename from src/legacy/ui/public/agg_types/buckets/create_filter/terms.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/terms.ts index 5bd770e672786..e0d1f91c1e16a 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/terms.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/create_filter/terms.ts @@ -18,7 +18,7 @@ */ import { IBucketAggConfig } from '../_bucket_agg_type'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters } from '../../../../../../../../plugins/data/public'; export const createFilterTerms = (aggConfig: IBucketAggConfig, key: string, params: any) => { const field = aggConfig.params.field; diff --git a/src/legacy/ui/public/agg_types/buckets/date_histogram.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/date_histogram.ts similarity index 95% rename from src/legacy/ui/public/agg_types/buckets/date_histogram.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/date_histogram.ts index 33672b54b1f2e..dc0f9baa6d0cc 100644 --- a/src/legacy/ui/public/agg_types/buckets/date_histogram.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/date_histogram.ts @@ -22,19 +22,17 @@ import moment from 'moment-timezone'; import { i18n } from '@kbn/i18n'; import { npStart } from 'ui/new_platform'; +import { timefilter } from 'ui/timefilter'; +import { TimeBuckets } from 'ui/time_buckets'; import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; import { createFilterDateHistogram } from './create_filter/date_histogram'; import { intervalOptions } from './_interval_options'; -import { timefilter } from '../../timefilter'; -import { dateHistogramInterval } from '../../../../core_plugins/data/public'; +import { dateHistogramInterval } from '../../../../common'; import { writeParams } from '../agg_params'; import { isMetricAggType } from '../metrics/metric_agg_type'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; - -// @ts-ignore -import { TimeBuckets } from '../../time_buckets'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; const detectedTimezone = moment.tz.guess(); const tzOffset = moment().format('Z'); @@ -67,7 +65,7 @@ export function isDateHistogramBucketAggConfig(agg: any): agg is IBucketDateHist export const dateHistogramBucketAgg = new BucketAggType({ name: BUCKET_TYPES.DATE_HISTOGRAM, - title: i18n.translate('common.ui.aggTypes.buckets.dateHistogramTitle', { + title: i18n.translate('data.search.aggs.buckets.dateHistogramTitle', { defaultMessage: 'Date Histogram', }), ordered: { @@ -81,7 +79,7 @@ export const dateHistogramBucketAgg = new BucketAggType { describe('getRequestAggs', () => { describe('initial aggregation creation', () => { - let aggConfigs: AggConfigs; + let aggConfigs: IAggConfigs; let geoHashGridAgg: IBucketGeoHashGridAggConfig; beforeEach(() => { diff --git a/src/legacy/ui/public/agg_types/buckets/geo_hash.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/geo_hash.ts similarity index 96% rename from src/legacy/ui/public/agg_types/buckets/geo_hash.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/geo_hash.ts index b2519df6fb175..afd4e18dd266c 100644 --- a/src/legacy/ui/public/agg_types/buckets/geo_hash.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/geo_hash.ts @@ -19,9 +19,9 @@ import { i18n } from '@kbn/i18n'; import { geohashColumns } from 'ui/vis/map/decode_geo_hash'; -import chrome from '../../chrome'; +import chrome from 'ui/chrome'; import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; import { geoContains, scaleBounds, GeoBoundingBox } from './lib/geo_utils'; import { BUCKET_TYPES } from './bucket_agg_types'; @@ -68,7 +68,7 @@ function getPrecision(val: string) { const isOutsideCollar = (bounds: GeoBoundingBox, collar: MapCollar) => bounds && collar && !geoContains(collar, bounds); -const geohashGridTitle = i18n.translate('common.ui.aggTypes.buckets.geohashGridTitle', { +const geohashGridTitle = i18n.translate('data.search.aggs.buckets.geohashGridTitle', { defaultMessage: 'Geohash', }); diff --git a/src/legacy/ui/public/agg_types/buckets/geo_tile.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/geo_tile.ts similarity index 92% rename from src/legacy/ui/public/agg_types/buckets/geo_tile.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/geo_tile.ts index ef71e3947566a..57e8f6e8c5ded 100644 --- a/src/legacy/ui/public/agg_types/buckets/geo_tile.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/geo_tile.ts @@ -23,11 +23,11 @@ import { AggConfigOptions } from '../agg_config'; import { BucketAggType } from './_bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; import { IBucketAggConfig } from './_bucket_agg_type'; import { METRIC_TYPES } from '../metrics/metric_agg_types'; -const geotileGridTitle = i18n.translate('common.ui.aggTypes.buckets.geotileGridTitle', { +const geotileGridTitle = i18n.translate('data.search.aggs.buckets.geotileGridTitle', { defaultMessage: 'Geotile', }); diff --git a/src/legacy/ui/public/agg_types/buckets/histogram.test.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/histogram.test.ts similarity index 100% rename from src/legacy/ui/public/agg_types/buckets/histogram.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/histogram.test.ts diff --git a/src/legacy/ui/public/agg_types/buckets/histogram.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/histogram.ts similarity index 96% rename from src/legacy/ui/public/agg_types/buckets/histogram.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/histogram.ts index 44327c7c19e6d..f7e9ef45961e0 100644 --- a/src/legacy/ui/public/agg_types/buckets/histogram.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/histogram.ts @@ -24,7 +24,7 @@ import { toastNotifications } from 'ui/notify'; import { npStart } from 'ui/new_platform'; import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; import { createFilterHistogram } from './create_filter/histogram'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; import { BUCKET_TYPES } from './bucket_agg_types'; export interface AutoBounds { @@ -41,7 +41,7 @@ const getUIConfig = () => npStart.core.uiSettings; export const histogramBucketAgg = new BucketAggType({ name: BUCKET_TYPES.HISTOGRAM, - title: i18n.translate('common.ui.aggTypes.buckets.histogramTitle', { + title: i18n.translate('data.search.aggs.buckets.histogramTitle', { defaultMessage: 'Histogram', }), ordered: {}, @@ -117,7 +117,7 @@ export const histogramBucketAgg = new BucketAggType({ .catch((e: Error) => { if (e.name === 'AbortError') return; toastNotifications.addWarning( - i18n.translate('common.ui.aggTypes.histogram.missingMaxMinValuesWarning', { + i18n.translate('data.search.aggs.histogram.missingMaxMinValuesWarning', { defaultMessage: 'Unable to retrieve max and min values to auto-scale histogram buckets. This may lead to poor visualization performance.', }) diff --git a/src/legacy/ui/public/agg_types/buckets/ip_range.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/ip_range.ts similarity index 93% rename from src/legacy/ui/public/agg_types/buckets/ip_range.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/ip_range.ts index 41141dabf507c..e5497bef49165 100644 --- a/src/legacy/ui/public/agg_types/buckets/ip_range.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/ip_range.ts @@ -25,9 +25,9 @@ import { BUCKET_TYPES } from './bucket_agg_types'; // @ts-ignore import { createFilterIpRange } from './create_filter/ip_range'; -import { KBN_FIELD_TYPES, fieldFormats } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES, fieldFormats } from '../../../../../../../plugins/data/public'; -const ipRangeTitle = i18n.translate('common.ui.aggTypes.buckets.ipRangeTitle', { +const ipRangeTitle = i18n.translate('data.search.aggs.buckets.ipRangeTitle', { defaultMessage: 'IPv4 Range', }); @@ -57,7 +57,7 @@ export const ipRangeBucketAgg = new BucketAggType({ return new IpRangeFormat(); }, makeLabel(aggConfig) { - return i18n.translate('common.ui.aggTypes.buckets.ipRangeLabel', { + return i18n.translate('data.search.aggs.buckets.ipRangeLabel', { defaultMessage: '{fieldName} IP ranges', values: { fieldName: aggConfig.getFieldDisplayName(), diff --git a/src/legacy/ui/public/agg_types/buckets/lib/cidr_mask.test.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/lib/cidr_mask.test.ts similarity index 100% rename from src/legacy/ui/public/agg_types/buckets/lib/cidr_mask.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/lib/cidr_mask.test.ts diff --git a/src/legacy/ui/public/agg_types/buckets/lib/cidr_mask.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/lib/cidr_mask.ts similarity index 95% rename from src/legacy/ui/public/agg_types/buckets/lib/cidr_mask.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/lib/cidr_mask.ts index aadbbc8c82276..30c4e400fb806 100644 --- a/src/legacy/ui/public/agg_types/buckets/lib/cidr_mask.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/lib/cidr_mask.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Ipv4Address } from '../../../../../../plugins/kibana_utils/public'; +import { Ipv4Address } from '../../../../../../../../plugins/kibana_utils/public'; const NUM_BITS = 32; diff --git a/src/legacy/ui/public/agg_types/buckets/lib/geo_utils.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/lib/geo_utils.ts similarity index 100% rename from src/legacy/ui/public/agg_types/buckets/lib/geo_utils.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/lib/geo_utils.ts diff --git a/src/legacy/ui/public/agg_types/buckets/migrate_include_exclude_format.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/migrate_include_exclude_format.ts similarity index 100% rename from src/legacy/ui/public/agg_types/buckets/migrate_include_exclude_format.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/migrate_include_exclude_format.ts diff --git a/src/legacy/ui/public/agg_types/buckets/range.test.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/range.test.ts similarity index 97% rename from src/legacy/ui/public/agg_types/buckets/range.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/range.test.ts index dd85c3b31939f..4c0fa7311461e 100644 --- a/src/legacy/ui/public/agg_types/buckets/range.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/range.test.ts @@ -19,7 +19,7 @@ import { AggConfigs } from '../agg_configs'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { fieldFormats } from '../../../../../plugins/data/public'; +import { fieldFormats } from '../../../../../../../plugins/data/public'; jest.mock('ui/new_platform'); diff --git a/src/legacy/ui/public/agg_types/buckets/range.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/range.ts similarity index 89% rename from src/legacy/ui/public/agg_types/buckets/range.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/range.ts index f24473e0c68aa..f35db2cc759bd 100644 --- a/src/legacy/ui/public/agg_types/buckets/range.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/range.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; import { BucketAggType } from './_bucket_agg_type'; -import { fieldFormats, KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { fieldFormats, KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; import { RangeKey } from './range_key'; import { createFilterRange } from './create_filter/range'; import { BUCKET_TYPES } from './bucket_agg_types'; @@ -27,7 +27,7 @@ import { BUCKET_TYPES } from './bucket_agg_types'; const keyCaches = new WeakMap(); const formats = new WeakMap(); -const rangeTitle = i18n.translate('common.ui.aggTypes.buckets.rangeTitle', { +const rangeTitle = i18n.translate('data.search.aggs.buckets.rangeTitle', { defaultMessage: 'Range', }); @@ -36,7 +36,7 @@ export const rangeBucketAgg = new BucketAggType({ title: rangeTitle, createFilter: createFilterRange, makeLabel(aggConfig) { - return i18n.translate('common.ui.aggTypes.buckets.rangesLabel', { + return i18n.translate('data.search.aggs.aggTypesLabel', { defaultMessage: '{fieldName} ranges', values: { fieldName: aggConfig.getFieldDisplayName(), @@ -69,7 +69,7 @@ export const rangeBucketAgg = new BucketAggType({ const format = agg.fieldOwnFormatter(); const gte = '\u2265'; const lt = '\u003c'; - return i18n.translate('common.ui.aggTypes.buckets.ranges.rangesFormatMessage', { + return i18n.translate('data.search.aggs.aggTypes.rangesFormatMessage', { defaultMessage: '{gte} {from} and {lt} {to}', values: { gte, diff --git a/src/legacy/ui/public/agg_types/buckets/range_key.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/range_key.ts similarity index 100% rename from src/legacy/ui/public/agg_types/buckets/range_key.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/range_key.ts diff --git a/src/legacy/ui/public/agg_types/buckets/significant_terms.test.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/significant_terms.test.ts similarity index 96% rename from src/legacy/ui/public/agg_types/buckets/significant_terms.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/significant_terms.test.ts index 8db9226e41eec..37b829bfc20fb 100644 --- a/src/legacy/ui/public/agg_types/buckets/significant_terms.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/significant_terms.test.ts @@ -18,6 +18,7 @@ */ import { AggConfigs } from '../index'; +import { IAggConfigs } from '../types'; import { BUCKET_TYPES } from './bucket_agg_types'; import { significantTermsBucketAgg } from './significant_terms'; import { IBucketAggConfig } from './_bucket_agg_type'; @@ -56,7 +57,7 @@ describe('Significant Terms Agg', () => { ); }; - const testSerializeAndWrite = (aggs: AggConfigs) => { + const testSerializeAndWrite = (aggs: IAggConfigs) => { const agg = aggs.aggs[0]; const { [BUCKET_TYPES.SIGNIFICANT_TERMS]: params } = agg.toDsl(); diff --git a/src/legacy/ui/public/agg_types/buckets/significant_terms.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/significant_terms.ts similarity index 82% rename from src/legacy/ui/public/agg_types/buckets/significant_terms.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/significant_terms.ts index 38ca0768d3bc1..bc6c63d569b11 100644 --- a/src/legacy/ui/public/agg_types/buckets/significant_terms.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/significant_terms.ts @@ -22,9 +22,9 @@ import { BucketAggType } from './_bucket_agg_type'; import { createFilterTerms } from './create_filter/terms'; import { isStringType, migrateIncludeExcludeFormat } from './migrate_include_exclude_format'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; -const significantTermsTitle = i18n.translate('common.ui.aggTypes.buckets.significantTermsTitle', { +const significantTermsTitle = i18n.translate('data.search.aggs.buckets.significantTermsTitle', { defaultMessage: 'Significant Terms', }); @@ -32,7 +32,7 @@ export const significantTermsBucketAgg = new BucketAggType({ name: BUCKET_TYPES.SIGNIFICANT_TERMS, title: significantTermsTitle, makeLabel(aggConfig) { - return i18n.translate('common.ui.aggTypes.buckets.significantTermsLabel', { + return i18n.translate('data.search.aggs.buckets.significantTermsLabel', { defaultMessage: 'Top {size} unusual terms in {fieldName}', values: { size: aggConfig.params.size, @@ -54,7 +54,7 @@ export const significantTermsBucketAgg = new BucketAggType({ }, { name: 'exclude', - displayName: i18n.translate('common.ui.aggTypes.buckets.significantTerms.excludeLabel', { + displayName: i18n.translate('data.search.aggs.buckets.significantTerms.excludeLabel', { defaultMessage: 'Exclude', }), type: 'string', @@ -64,7 +64,7 @@ export const significantTermsBucketAgg = new BucketAggType({ }, { name: 'include', - displayName: i18n.translate('common.ui.aggTypes.buckets.significantTerms.includeLabel', { + displayName: i18n.translate('data.search.aggs.buckets.significantTerms.includeLabel', { defaultMessage: 'Include', }), type: 'string', diff --git a/src/legacy/ui/public/agg_types/buckets/terms.test.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/terms.test.ts similarity index 100% rename from src/legacy/ui/public/agg_types/buckets/terms.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/terms.test.ts diff --git a/src/legacy/ui/public/agg_types/buckets/terms.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/terms.ts similarity index 85% rename from src/legacy/ui/public/agg_types/buckets/terms.ts rename to src/legacy/core_plugins/data/public/search/aggs/buckets/terms.ts index 4ced1417402b5..b41b16af122fa 100644 --- a/src/legacy/ui/public/agg_types/buckets/terms.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/terms.ts @@ -19,19 +19,20 @@ import { noop } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { - getRequestInspectorStats, - getResponseInspectorStats, -} from '../../../../core_plugins/data/public'; +import { getRequestInspectorStats, getResponseInspectorStats } from '../../../index'; import { BucketAggType } from './_bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; import { IBucketAggConfig } from './_bucket_agg_type'; import { createFilterTerms } from './create_filter/terms'; import { isStringType, migrateIncludeExcludeFormat } from './migrate_include_exclude_format'; -import { AggConfigs } from '../agg_configs'; +import { IAggConfigs } from '../agg_configs'; -import { Adapters } from '../../../../../plugins/inspector/public'; -import { ISearchSource, fieldFormats, KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { Adapters } from '../../../../../../../plugins/inspector/public'; +import { + ISearchSource, + fieldFormats, + KBN_FIELD_TYPES, +} from '../../../../../../../plugins/data/public'; import { buildOtherBucketAgg, @@ -68,7 +69,7 @@ const [orderAggSchema] = new Schemas([ }, ]).all; -const termsTitle = i18n.translate('common.ui.aggTypes.buckets.termsTitle', { +const termsTitle = i18n.translate('data.search.aggs.buckets.termsTitle', { defaultMessage: 'Terms', }); @@ -98,7 +99,7 @@ export const termsBucketAgg = new BucketAggType({ createFilter: createFilterTerms, postFlightRequest: async ( resp: any, - aggConfigs: AggConfigs, + aggConfigs: IAggConfigs, aggConfig: IBucketAggConfig, searchSource: ISearchSource, inspectorAdapters: Adapters, @@ -113,11 +114,11 @@ export const termsBucketAgg = new BucketAggType({ nestedSearchSource.setField('aggs', filterAgg); const request = inspectorAdapters.requests.start( - i18n.translate('common.ui.aggTypes.buckets.terms.otherBucketTitle', { + i18n.translate('data.search.aggs.buckets.terms.otherBucketTitle', { defaultMessage: 'Other bucket', }), { - description: i18n.translate('common.ui.aggTypes.buckets.terms.otherBucketDescription', { + description: i18n.translate('data.search.aggs.buckets.terms.otherBucketDescription', { defaultMessage: 'This request counts the number of documents that fall ' + 'outside the criterion of the data buckets.', @@ -212,13 +213,13 @@ export const termsBucketAgg = new BucketAggType({ default: 'desc', options: [ { - text: i18n.translate('common.ui.aggTypes.buckets.terms.orderDescendingTitle', { + text: i18n.translate('data.search.aggs.buckets.terms.orderDescendingTitle', { defaultMessage: 'Descending', }), value: 'desc', }, { - text: i18n.translate('common.ui.aggTypes.buckets.terms.orderAscendingTitle', { + text: i18n.translate('data.search.aggs.buckets.terms.orderAscendingTitle', { defaultMessage: 'Ascending', }), value: 'asc', @@ -238,10 +239,10 @@ export const termsBucketAgg = new BucketAggType({ { name: 'otherBucketLabel', type: 'string', - default: i18n.translate('common.ui.aggTypes.buckets.terms.otherBucketLabel', { + default: i18n.translate('data.search.aggs.buckets.terms.otherBucketLabel', { defaultMessage: 'Other', }), - displayName: i18n.translate('common.ui.aggTypes.otherBucket.labelForOtherBucketLabel', { + displayName: i18n.translate('data.search.aggs.otherBucket.labelForOtherBucketLabel', { defaultMessage: 'Label for other bucket', }), shouldShow: agg => agg.getParam('otherBucket'), @@ -254,13 +255,13 @@ export const termsBucketAgg = new BucketAggType({ }, { name: 'missingBucketLabel', - default: i18n.translate('common.ui.aggTypes.buckets.terms.missingBucketLabel', { + default: i18n.translate('data.search.aggs.buckets.terms.missingBucketLabel', { defaultMessage: 'Missing', description: `Default label used in charts when documents are missing a field. Visible when you create a chart with a terms aggregation and enable "Show missing values"`, }), type: 'string', - displayName: i18n.translate('common.ui.aggTypes.otherBucket.labelForMissingValuesLabel', { + displayName: i18n.translate('data.search.aggs.otherBucket.labelForMissingValuesLabel', { defaultMessage: 'Label for missing values', }), shouldShow: agg => agg.getParam('missingBucket'), @@ -268,7 +269,7 @@ export const termsBucketAgg = new BucketAggType({ }, { name: 'exclude', - displayName: i18n.translate('common.ui.aggTypes.buckets.terms.excludeLabel', { + displayName: i18n.translate('data.search.aggs.buckets.terms.excludeLabel', { defaultMessage: 'Exclude', }), type: 'string', @@ -278,7 +279,7 @@ export const termsBucketAgg = new BucketAggType({ }, { name: 'include', - displayName: i18n.translate('common.ui.aggTypes.buckets.terms.includeLabel', { + displayName: i18n.translate('data.search.aggs.buckets.terms.includeLabel', { defaultMessage: 'Include', }), type: 'string', diff --git a/src/legacy/ui/public/agg_types/filter/agg_type_filters.test.ts b/src/legacy/core_plugins/data/public/search/aggs/filter/agg_type_filters.test.ts similarity index 88% rename from src/legacy/ui/public/agg_types/filter/agg_type_filters.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/filter/agg_type_filters.test.ts index 0344f304877f2..cc1288d339692 100644 --- a/src/legacy/ui/public/agg_types/filter/agg_type_filters.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/filter/agg_type_filters.test.ts @@ -17,9 +17,10 @@ * under the License. */ -import { IndexPattern } from '../../../../../plugins/data/public'; +import { IndexPattern } from '../../../../../../../plugins/data/public'; import { AggTypeFilters } from './agg_type_filters'; -import { AggConfig, AggType } from '..'; +import { AggConfig } from '..'; +import { IAggType } from '../types'; describe('AggTypeFilters', () => { let registry: AggTypeFilters; @@ -31,13 +32,13 @@ describe('AggTypeFilters', () => { }); it('should filter nothing without registered filters', async () => { - const aggTypes = [{ name: 'count' }, { name: 'sum' }] as AggType[]; + const aggTypes = [{ name: 'count' }, { name: 'sum' }] as IAggType[]; const filtered = registry.filter(aggTypes, indexPattern, aggConfig); expect(filtered).toEqual(aggTypes); }); it('should pass all aggTypes to the registered filter', async () => { - const aggTypes = [{ name: 'count' }, { name: 'sum' }] as AggType[]; + const aggTypes = [{ name: 'count' }, { name: 'sum' }] as IAggType[]; const filter = jest.fn(); registry.addFilter(filter); registry.filter(aggTypes, indexPattern, aggConfig); @@ -46,7 +47,7 @@ describe('AggTypeFilters', () => { }); it('should allow registered filters to filter out aggTypes', async () => { - const aggTypes = [{ name: 'count' }, { name: 'sum' }, { name: 'avg' }] as AggType[]; + const aggTypes = [{ name: 'count' }, { name: 'sum' }, { name: 'avg' }] as IAggType[]; let filtered = registry.filter(aggTypes, indexPattern, aggConfig); expect(filtered).toEqual(aggTypes); diff --git a/src/legacy/ui/public/agg_types/filter/agg_type_filters.ts b/src/legacy/core_plugins/data/public/search/aggs/filter/agg_type_filters.ts similarity index 92% rename from src/legacy/ui/public/agg_types/filter/agg_type_filters.ts rename to src/legacy/core_plugins/data/public/search/aggs/filter/agg_type_filters.ts index 2cc4a6e962214..d3b38ce041d7e 100644 --- a/src/legacy/ui/public/agg_types/filter/agg_type_filters.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/filter/agg_type_filters.ts @@ -17,12 +17,12 @@ * under the License. */ import { IndexPattern } from 'src/plugins/data/public'; -import { AggType, AggConfig } from '..'; +import { IAggConfig, IAggType } from '../types'; type AggTypeFilter = ( - aggType: AggType, + aggType: IAggType, indexPattern: IndexPattern, - aggConfig: AggConfig + aggConfig: IAggConfig ) => boolean; /** @@ -49,7 +49,7 @@ class AggTypeFilters { * @param aggConfig The aggConfig for which the returning list will be used. * @return A filtered list of the passed aggTypes. */ - public filter(aggTypes: AggType[], indexPattern: IndexPattern, aggConfig: AggConfig) { + public filter(aggTypes: IAggType[], indexPattern: IndexPattern, aggConfig: IAggConfig) { const allFilters = Array.from(this.filters); const allowedAggTypes = aggTypes.filter(aggType => { const isAggTypeAllowed = allFilters.every(filter => filter(aggType, indexPattern, aggConfig)); diff --git a/src/legacy/ui/public/agg_types/filter/index.ts b/src/legacy/core_plugins/data/public/search/aggs/filter/index.ts similarity index 100% rename from src/legacy/ui/public/agg_types/filter/index.ts rename to src/legacy/core_plugins/data/public/search/aggs/filter/index.ts diff --git a/src/legacy/ui/public/agg_types/filter/prop_filter.test.ts b/src/legacy/core_plugins/data/public/search/aggs/filter/prop_filter.test.ts similarity index 100% rename from src/legacy/ui/public/agg_types/filter/prop_filter.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/filter/prop_filter.test.ts diff --git a/src/legacy/ui/public/agg_types/filter/prop_filter.ts b/src/legacy/core_plugins/data/public/search/aggs/filter/prop_filter.ts similarity index 100% rename from src/legacy/ui/public/agg_types/filter/prop_filter.ts rename to src/legacy/core_plugins/data/public/search/aggs/filter/prop_filter.ts diff --git a/src/legacy/ui/public/agg_types/index.test.ts b/src/legacy/core_plugins/data/public/search/aggs/index.test.ts similarity index 100% rename from src/legacy/ui/public/agg_types/index.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/index.test.ts diff --git a/src/legacy/core_plugins/data/public/search/aggs/index.ts b/src/legacy/core_plugins/data/public/search/aggs/index.ts new file mode 100644 index 0000000000000..0fef7f38aae74 --- /dev/null +++ b/src/legacy/core_plugins/data/public/search/aggs/index.ts @@ -0,0 +1,52 @@ +/* + * 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 { aggTypes } from './agg_types'; +export { AggType } from './agg_type'; +export { AggConfig } from './agg_config'; +export { AggConfigs } from './agg_configs'; +export { FieldParamType } from './param_types'; +export { MetricAggType } from './metrics/metric_agg_type'; +export { AggTypeFilters } from './filter'; +export { aggTypeFieldFilters, AggTypeFieldFilters } from './param_types/filter'; +export { + parentPipelineAggHelper, + parentPipelineType, +} from './metrics/lib/parent_pipeline_agg_helper'; +export { + siblingPipelineAggHelper, + siblingPipelineType, +} from './metrics/lib/sibling_pipeline_agg_helper'; + +// static code +export { AggParamType } from './param_types/agg'; +export { AggGroupNames, aggGroupNamesMap } from './agg_groups'; +export { intervalOptions } from './buckets/_interval_options'; // only used in Discover +export { isDateHistogramBucketAggConfig, setBounds } from './buckets/date_histogram'; +export { termsAggFilter } from './buckets/terms'; +export { isType, isStringType } from './buckets/migrate_include_exclude_format'; +export { CidrMask } from './buckets/lib/cidr_mask'; +export { convertDateRangeToString } from './buckets/date_range'; +export { convertIPRangeToString } from './buckets/ip_range'; +export { aggTypeFilters, propFilter } from './filter'; +export { OptionedParamType } from './param_types/optioned'; +export { isValidJson, isValidInterval } from './utils'; +export { BUCKET_TYPES } from './buckets/bucket_agg_types'; +export { METRIC_TYPES } from './metrics/metric_agg_types'; +export { ISchemas, Schema, Schemas } from './schemas'; diff --git a/src/legacy/ui/public/agg_types/metrics/avg.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/avg.ts similarity index 85% rename from src/legacy/ui/public/agg_types/metrics/avg.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/avg.ts index 0222a8e543223..b80671a43d2af 100644 --- a/src/legacy/ui/public/agg_types/metrics/avg.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/avg.ts @@ -20,9 +20,9 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; -const averageTitle = i18n.translate('common.ui.aggTypes.metrics.averageTitle', { +const averageTitle = i18n.translate('data.search.aggs.metrics.averageTitle', { defaultMessage: 'Average', }); @@ -30,7 +30,7 @@ export const avgMetricAgg = new MetricAggType({ name: METRIC_TYPES.AVG, title: averageTitle, makeLabel: aggConfig => { - return i18n.translate('common.ui.aggTypes.metrics.averageLabel', { + return i18n.translate('data.search.aggs.metrics.averageLabel', { defaultMessage: 'Average {field}', values: { field: aggConfig.getFieldDisplayName() }, }); diff --git a/src/legacy/ui/public/agg_types/metrics/bucket_avg.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/bucket_avg.ts similarity index 91% rename from src/legacy/ui/public/agg_types/metrics/bucket_avg.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/bucket_avg.ts index 7142546dbd494..9fb28f8631bc6 100644 --- a/src/legacy/ui/public/agg_types/metrics/bucket_avg.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/bucket_avg.ts @@ -25,11 +25,11 @@ import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; import { METRIC_TYPES } from './metric_agg_types'; -const overallAverageLabel = i18n.translate('common.ui.aggTypes.metrics.overallAverageLabel', { +const overallAverageLabel = i18n.translate('data.search.aggs.metrics.overallAverageLabel', { defaultMessage: 'overall average', }); -const averageBucketTitle = i18n.translate('common.ui.aggTypes.metrics.averageBucketTitle', { +const averageBucketTitle = i18n.translate('data.search.aggs.metrics.averageBucketTitle', { defaultMessage: 'Average Bucket', }); diff --git a/src/legacy/ui/public/agg_types/metrics/bucket_max.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/bucket_max.ts similarity index 89% rename from src/legacy/ui/public/agg_types/metrics/bucket_max.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/bucket_max.ts index aa5b0521709a5..83837f0de5114 100644 --- a/src/legacy/ui/public/agg_types/metrics/bucket_max.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/bucket_max.ts @@ -24,11 +24,11 @@ import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; import { METRIC_TYPES } from './metric_agg_types'; -const overallMaxLabel = i18n.translate('common.ui.aggTypes.metrics.overallMaxLabel', { +const overallMaxLabel = i18n.translate('data.search.aggs.metrics.overallMaxLabel', { defaultMessage: 'overall max', }); -const maxBucketTitle = i18n.translate('common.ui.aggTypes.metrics.maxBucketTitle', { +const maxBucketTitle = i18n.translate('data.search.aggs.metrics.maxBucketTitle', { defaultMessage: 'Max Bucket', }); diff --git a/src/legacy/ui/public/agg_types/metrics/bucket_min.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/bucket_min.ts similarity index 89% rename from src/legacy/ui/public/agg_types/metrics/bucket_min.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/bucket_min.ts index b5c0b8865e106..d96197693dc2e 100644 --- a/src/legacy/ui/public/agg_types/metrics/bucket_min.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/bucket_min.ts @@ -22,11 +22,11 @@ import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; import { METRIC_TYPES } from './metric_agg_types'; -const overallMinLabel = i18n.translate('common.ui.aggTypes.metrics.overallMinLabel', { +const overallMinLabel = i18n.translate('data.search.aggs.metrics.overallMinLabel', { defaultMessage: 'overall min', }); -const minBucketTitle = i18n.translate('common.ui.aggTypes.metrics.minBucketTitle', { +const minBucketTitle = i18n.translate('data.search.aggs.metrics.minBucketTitle', { defaultMessage: 'Min Bucket', }); diff --git a/src/legacy/ui/public/agg_types/metrics/bucket_sum.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/bucket_sum.ts similarity index 89% rename from src/legacy/ui/public/agg_types/metrics/bucket_sum.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/bucket_sum.ts index d4faa81c4041c..1f9392c5bec35 100644 --- a/src/legacy/ui/public/agg_types/metrics/bucket_sum.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/bucket_sum.ts @@ -23,11 +23,11 @@ import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; import { METRIC_TYPES } from './metric_agg_types'; -const overallSumLabel = i18n.translate('common.ui.aggTypes.metrics.overallSumLabel', { +const overallSumLabel = i18n.translate('data.search.aggs.metrics.overallSumLabel', { defaultMessage: 'overall sum', }); -const sumBucketTitle = i18n.translate('common.ui.aggTypes.metrics.sumBucketTitle', { +const sumBucketTitle = i18n.translate('data.search.aggs.metrics.sumBucketTitle', { defaultMessage: 'Sum Bucket', }); diff --git a/src/legacy/ui/public/agg_types/metrics/cardinality.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/cardinality.ts similarity index 86% rename from src/legacy/ui/public/agg_types/metrics/cardinality.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/cardinality.ts index c69ffae3b4871..147e925521088 100644 --- a/src/legacy/ui/public/agg_types/metrics/cardinality.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/cardinality.ts @@ -21,9 +21,9 @@ import { i18n } from '@kbn/i18n'; import { npStart } from 'ui/new_platform'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; -const uniqueCountTitle = i18n.translate('common.ui.aggTypes.metrics.uniqueCountTitle', { +const uniqueCountTitle = i18n.translate('data.search.aggs.metrics.uniqueCountTitle', { defaultMessage: 'Unique Count', }); @@ -31,7 +31,7 @@ export const cardinalityMetricAgg = new MetricAggType({ name: METRIC_TYPES.CARDINALITY, title: uniqueCountTitle, makeLabel(aggConfig) { - return i18n.translate('common.ui.aggTypes.metrics.uniqueCountLabel', { + return i18n.translate('data.search.aggs.metrics.uniqueCountLabel', { defaultMessage: 'Unique count of {field}', values: { field: aggConfig.getFieldDisplayName() }, }); diff --git a/src/legacy/ui/public/agg_types/metrics/count.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/count.ts similarity index 87% rename from src/legacy/ui/public/agg_types/metrics/count.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/count.ts index 22a939cd9a3fd..14a9bd073ff2b 100644 --- a/src/legacy/ui/public/agg_types/metrics/count.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/count.ts @@ -19,18 +19,18 @@ import { i18n } from '@kbn/i18n'; import { npStart } from 'ui/new_platform'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; export const countMetricAgg = new MetricAggType({ name: METRIC_TYPES.COUNT, - title: i18n.translate('common.ui.aggTypes.metrics.countTitle', { + title: i18n.translate('data.search.aggs.metrics.countTitle', { defaultMessage: 'Count', }), hasNoDsl: true, makeLabel() { - return i18n.translate('common.ui.aggTypes.metrics.countLabel', { + return i18n.translate('data.search.aggs.metrics.countLabel', { defaultMessage: 'Count', }); }, diff --git a/src/legacy/ui/public/agg_types/metrics/cumulative_sum.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/cumulative_sum.ts similarity index 88% rename from src/legacy/ui/public/agg_types/metrics/cumulative_sum.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/cumulative_sum.ts index bad2de8cb16dc..a5d02459900bb 100644 --- a/src/legacy/ui/public/agg_types/metrics/cumulative_sum.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/cumulative_sum.ts @@ -23,11 +23,11 @@ import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; -const cumulativeSumLabel = i18n.translate('common.ui.aggTypes.metrics.cumulativeSumLabel', { +const cumulativeSumLabel = i18n.translate('data.search.aggs.metrics.cumulativeSumLabel', { defaultMessage: 'cumulative sum', }); -const cumulativeSumTitle = i18n.translate('common.ui.aggTypes.metrics.cumulativeSumTitle', { +const cumulativeSumTitle = i18n.translate('data.search.aggs.metrics.cumulativeSumTitle', { defaultMessage: 'Cumulative Sum', }); diff --git a/src/legacy/ui/public/agg_types/metrics/derivative.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/derivative.ts similarity index 89% rename from src/legacy/ui/public/agg_types/metrics/derivative.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/derivative.ts index 42921621a2933..1169a527b0668 100644 --- a/src/legacy/ui/public/agg_types/metrics/derivative.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/derivative.ts @@ -23,11 +23,11 @@ import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; -const derivativeLabel = i18n.translate('common.ui.aggTypes.metrics.derivativeLabel', { +const derivativeLabel = i18n.translate('data.search.aggs.metrics.derivativeLabel', { defaultMessage: 'derivative', }); -const derivativeTitle = i18n.translate('common.ui.aggTypes.metrics.derivativeTitle', { +const derivativeTitle = i18n.translate('data.search.aggs.metrics.derivativeTitle', { defaultMessage: 'Derivative', }); diff --git a/src/legacy/ui/public/agg_types/metrics/geo_bounds.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/geo_bounds.ts similarity index 84% rename from src/legacy/ui/public/agg_types/metrics/geo_bounds.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/geo_bounds.ts index b8ce03cdf11ec..53bc72f9ce1da 100644 --- a/src/legacy/ui/public/agg_types/metrics/geo_bounds.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/geo_bounds.ts @@ -20,13 +20,13 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; -const geoBoundsTitle = i18n.translate('common.ui.aggTypes.metrics.geoBoundsTitle', { +const geoBoundsTitle = i18n.translate('data.search.aggs.metrics.geoBoundsTitle', { defaultMessage: 'Geo Bounds', }); -const geoBoundsLabel = i18n.translate('common.ui.aggTypes.metrics.geoBoundsLabel', { +const geoBoundsLabel = i18n.translate('data.search.aggs.metrics.geoBoundsLabel', { defaultMessage: 'Geo Bounds', }); diff --git a/src/legacy/ui/public/agg_types/metrics/geo_centroid.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/geo_centroid.ts similarity index 84% rename from src/legacy/ui/public/agg_types/metrics/geo_centroid.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/geo_centroid.ts index 5313e31796a5b..a79b2b34ad1ca 100644 --- a/src/legacy/ui/public/agg_types/metrics/geo_centroid.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/geo_centroid.ts @@ -20,13 +20,13 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; -const geoCentroidTitle = i18n.translate('common.ui.aggTypes.metrics.geoCentroidTitle', { +const geoCentroidTitle = i18n.translate('data.search.aggs.metrics.geoCentroidTitle', { defaultMessage: 'Geo Centroid', }); -const geoCentroidLabel = i18n.translate('common.ui.aggTypes.metrics.geoCentroidLabel', { +const geoCentroidLabel = i18n.translate('data.search.aggs.metrics.geoCentroidLabel', { defaultMessage: 'Geo Centroid', }); diff --git a/src/legacy/ui/public/agg_types/metrics/lib/get_response_agg_config_class.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/lib/get_response_agg_config_class.ts similarity index 100% rename from src/legacy/ui/public/agg_types/metrics/lib/get_response_agg_config_class.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/lib/get_response_agg_config_class.ts diff --git a/src/legacy/ui/public/agg_types/metrics/lib/make_nested_label.test.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/lib/make_nested_label.test.ts similarity index 100% rename from src/legacy/ui/public/agg_types/metrics/lib/make_nested_label.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/lib/make_nested_label.test.ts diff --git a/src/legacy/ui/public/agg_types/metrics/lib/make_nested_label.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/lib/make_nested_label.ts similarity index 100% rename from src/legacy/ui/public/agg_types/metrics/lib/make_nested_label.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/lib/make_nested_label.ts diff --git a/src/legacy/ui/public/agg_types/metrics/lib/nested_agg_helpers.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/lib/nested_agg_helpers.ts similarity index 100% rename from src/legacy/ui/public/agg_types/metrics/lib/nested_agg_helpers.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/lib/nested_agg_helpers.ts diff --git a/src/legacy/ui/public/agg_types/metrics/lib/ordinal_suffix.test.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/lib/ordinal_suffix.test.ts similarity index 100% rename from src/legacy/ui/public/agg_types/metrics/lib/ordinal_suffix.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/lib/ordinal_suffix.test.ts diff --git a/src/legacy/ui/public/agg_types/metrics/lib/ordinal_suffix.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/lib/ordinal_suffix.ts similarity index 100% rename from src/legacy/ui/public/agg_types/metrics/lib/ordinal_suffix.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/lib/ordinal_suffix.ts diff --git a/src/legacy/ui/public/agg_types/metrics/lib/parent_pipeline_agg_helper.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts similarity index 94% rename from src/legacy/ui/public/agg_types/metrics/lib/parent_pipeline_agg_helper.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts index 4d558e50304e6..0d1b2472bb8e2 100644 --- a/src/legacy/ui/public/agg_types/metrics/lib/parent_pipeline_agg_helper.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts @@ -36,7 +36,7 @@ const metricAggFilter = [ '!geo_centroid', ]; -const metricAggTitle = i18n.translate('common.ui.aggTypes.metrics.metricAggTitle', { +const metricAggTitle = i18n.translate('data.search.aggs.metrics.metricAggTitle', { defaultMessage: 'Metric agg', }); @@ -51,7 +51,7 @@ const [metricAggSchema] = new Schemas([ ]).all; const parentPipelineType = i18n.translate( - 'common.ui.aggTypes.metrics.parentPipelineAggregationsSubtypeTitle', + 'data.search.aggs.metrics.parentPipelineAggregationsSubtypeTitle', { defaultMessage: 'Parent Pipeline Aggregations', } diff --git a/src/legacy/ui/public/agg_types/metrics/lib/parent_pipeline_agg_writer.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_writer.ts similarity index 94% rename from src/legacy/ui/public/agg_types/metrics/lib/parent_pipeline_agg_writer.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_writer.ts index 684fe721a754a..bc0359b2a213d 100644 --- a/src/legacy/ui/public/agg_types/metrics/lib/parent_pipeline_agg_writer.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_writer.ts @@ -17,13 +17,13 @@ * under the License. */ -import { AggConfigs } from '../../agg_configs'; +import { IAggConfigs } from '../../agg_configs'; import { IMetricAggConfig } from '../metric_agg_type'; export const parentPipelineAggWriter = ( agg: IMetricAggConfig, output: Record, - aggConfigs?: AggConfigs + aggConfigs?: IAggConfigs ): void => { const customMetric = agg.getParam('customMetric'); const metricAgg = agg.getParam('metricAgg'); diff --git a/src/legacy/ui/public/agg_types/metrics/lib/sibling_pipeline_agg_helper.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts similarity index 93% rename from src/legacy/ui/public/agg_types/metrics/lib/sibling_pipeline_agg_helper.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts index 9dd737bd6708e..3956bda1812ad 100644 --- a/src/legacy/ui/public/agg_types/metrics/lib/sibling_pipeline_agg_helper.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts @@ -47,7 +47,7 @@ const [metricAggSchema] = new Schemas([ { group: 'none', name: 'metricAgg', - title: i18n.translate('common.ui.aggTypes.metrics.metricAggTitle', { + title: i18n.translate('data.search.aggs.metrics.metricAggTitle', { defaultMessage: 'Metric agg', }), aggFilter: metricAggFilter, @@ -57,7 +57,7 @@ const [metricAggSchema] = new Schemas([ const [bucketAggSchema] = new Schemas([ { group: 'none', - title: i18n.translate('common.ui.aggTypes.metrics.bucketAggTitle', { + title: i18n.translate('data.search.aggs.metrics.bucketAggTitle', { defaultMessage: 'Bucket agg', }), name: 'bucketAgg', @@ -66,7 +66,7 @@ const [bucketAggSchema] = new Schemas([ ]).all; const siblingPipelineType = i18n.translate( - 'common.ui.aggTypes.metrics.siblingPipelineAggregationsSubtypeTitle', + 'data.search.aggs.metrics.siblingPipelineAggregationsSubtypeTitle', { defaultMessage: 'Sibling pipeline aggregations', } diff --git a/src/legacy/ui/public/agg_types/metrics/lib/sibling_pipeline_agg_writer.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_writer.ts similarity index 100% rename from src/legacy/ui/public/agg_types/metrics/lib/sibling_pipeline_agg_writer.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_writer.ts diff --git a/src/legacy/ui/public/agg_types/metrics/max.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/max.ts similarity index 86% rename from src/legacy/ui/public/agg_types/metrics/max.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/max.ts index 5c43511acee72..d561788936b51 100644 --- a/src/legacy/ui/public/agg_types/metrics/max.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/max.ts @@ -20,9 +20,9 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; -const maxTitle = i18n.translate('common.ui.aggTypes.metrics.maxTitle', { +const maxTitle = i18n.translate('data.search.aggs.metrics.maxTitle', { defaultMessage: 'Max', }); @@ -30,7 +30,7 @@ export const maxMetricAgg = new MetricAggType({ name: METRIC_TYPES.MAX, title: maxTitle, makeLabel(aggConfig) { - return i18n.translate('common.ui.aggTypes.metrics.maxLabel', { + return i18n.translate('data.search.aggs.metrics.maxLabel', { defaultMessage: 'Max {field}', values: { field: aggConfig.getFieldDisplayName() }, }); diff --git a/src/legacy/ui/public/agg_types/metrics/median.test.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/median.test.ts similarity index 95% rename from src/legacy/ui/public/agg_types/metrics/median.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/median.test.ts index 819c24f135cdc..9affb0e3b2814 100644 --- a/src/legacy/ui/public/agg_types/metrics/median.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/median.test.ts @@ -17,13 +17,13 @@ * under the License. */ -import { AggConfigs } from '../agg_configs'; +import { AggConfigs, IAggConfigs } from '../agg_configs'; import { METRIC_TYPES } from './metric_agg_types'; jest.mock('ui/new_platform'); describe('AggTypeMetricMedianProvider class', () => { - let aggConfigs: AggConfigs; + let aggConfigs: IAggConfigs; beforeEach(() => { const field = { diff --git a/src/legacy/ui/public/agg_types/metrics/median.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/median.ts similarity index 88% rename from src/legacy/ui/public/agg_types/metrics/median.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/median.ts index 5792d4a7c2ba3..be080aaa5ee6f 100644 --- a/src/legacy/ui/public/agg_types/metrics/median.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/median.ts @@ -22,9 +22,9 @@ import { METRIC_TYPES } from './metric_agg_types'; // @ts-ignore import { percentilesMetricAgg } from './percentiles'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; -const medianTitle = i18n.translate('common.ui.aggTypes.metrics.medianTitle', { +const medianTitle = i18n.translate('data.search.aggs.metrics.medianTitle', { defaultMessage: 'Median', }); @@ -33,7 +33,7 @@ export const medianMetricAgg = new MetricAggType({ dslName: 'percentiles', title: medianTitle, makeLabel(aggConfig) { - return i18n.translate('common.ui.aggTypes.metrics.medianLabel', { + return i18n.translate('data.search.aggs.metrics.medianLabel', { defaultMessage: 'Median {field}', values: { field: aggConfig.getFieldDisplayName() }, }); diff --git a/src/legacy/ui/public/agg_types/metrics/metric_agg_type.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/metric_agg_type.ts similarity index 89% rename from src/legacy/ui/public/agg_types/metrics/metric_agg_type.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/metric_agg_type.ts index 5cd3dffb10b9d..3bae7b92618dc 100644 --- a/src/legacy/ui/public/agg_types/metrics/metric_agg_type.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/metric_agg_type.ts @@ -23,7 +23,8 @@ import { AggType, AggTypeConfig } from '../agg_type'; import { AggParamType } from '../param_types/agg'; import { AggConfig } from '../agg_config'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; +import { FilterFieldTypes } from '../param_types/field'; export interface IMetricAggConfig extends AggConfig { type: InstanceType; @@ -31,7 +32,7 @@ export interface IMetricAggConfig extends AggConfig { export interface MetricAggParam extends AggParamType { - filterFieldTypes?: KBN_FIELD_TYPES | KBN_FIELD_TYPES[] | '*'; + filterFieldTypes?: FilterFieldTypes; onlyAggregatable?: boolean; } @@ -43,6 +44,9 @@ interface MetricAggTypeConfig subtype?: string; } +// TODO need to make a more explicit interface for this +export type IMetricAggType = MetricAggType; + export class MetricAggType extends AggType< TMetricAggConfig, MetricAggParam @@ -83,7 +87,7 @@ export class MetricAggType { const field = { diff --git a/src/legacy/ui/public/agg_types/metrics/percentile_ranks.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/percentile_ranks.ts similarity index 89% rename from src/legacy/ui/public/agg_types/metrics/percentile_ranks.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/percentile_ranks.ts index cbd46e3f5b28d..38b47a7e97d2f 100644 --- a/src/legacy/ui/public/agg_types/metrics/percentile_ranks.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/percentile_ranks.ts @@ -24,7 +24,7 @@ import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_respons import { getPercentileValue } from './percentiles_get_value'; import { METRIC_TYPES } from './metric_agg_types'; -import { fieldFormats, KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { fieldFormats, KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; // required by the values editor @@ -41,7 +41,7 @@ const valueProps = { const customLabel = this.getParam('customLabel'); const label = customLabel || this.getFieldDisplayName(); - return i18n.translate('common.ui.aggTypes.metrics.percentileRanks.valuePropsLabel', { + return i18n.translate('data.search.aggs.metrics.percentileRanks.valuePropsLabel', { defaultMessage: 'Percentile rank {format} of "{label}"', values: { format: format.convert(this.key, 'text'), label }, }); @@ -50,11 +50,11 @@ const valueProps = { export const percentileRanksMetricAgg = new MetricAggType({ name: METRIC_TYPES.PERCENTILE_RANKS, - title: i18n.translate('common.ui.aggTypes.metrics.percentileRanksTitle', { + title: i18n.translate('data.search.aggs.metrics.percentileRanksTitle', { defaultMessage: 'Percentile Ranks', }), makeLabel(agg) { - return i18n.translate('common.ui.aggTypes.metrics.percentileRanksLabel', { + return i18n.translate('data.search.aggs.metrics.percentileRanksLabel', { defaultMessage: 'Percentile ranks of {field}', values: { field: agg.getFieldDisplayName() }, }); diff --git a/src/legacy/ui/public/agg_types/metrics/percentiles.test.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/percentiles.test.ts similarity index 95% rename from src/legacy/ui/public/agg_types/metrics/percentiles.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/percentiles.test.ts index c9f4bcc3862a0..dd1aaca973e47 100644 --- a/src/legacy/ui/public/agg_types/metrics/percentiles.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/percentiles.test.ts @@ -18,13 +18,13 @@ */ import { IPercentileAggConfig, percentilesMetricAgg } from './percentiles'; -import { AggConfigs } from '../agg_configs'; +import { AggConfigs, IAggConfigs } from '../agg_configs'; import { METRIC_TYPES } from './metric_agg_types'; jest.mock('ui/new_platform'); describe('AggTypesMetricsPercentilesProvider class', () => { - let aggConfigs: AggConfigs; + let aggConfigs: IAggConfigs; beforeEach(() => { const field = { diff --git a/src/legacy/ui/public/agg_types/metrics/percentiles.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/percentiles.ts similarity index 88% rename from src/legacy/ui/public/agg_types/metrics/percentiles.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/percentiles.ts index 040324d8da5df..39dc0d0f181e9 100644 --- a/src/legacy/ui/public/agg_types/metrics/percentiles.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/percentiles.ts @@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_response_agg_config_class'; import { getPercentileValue } from './percentiles_get_value'; @@ -36,7 +36,7 @@ const valueProps = { const customLabel = this.getParam('customLabel'); const label = customLabel || this.getFieldDisplayName(); - return i18n.translate('common.ui.aggTypes.metrics.percentiles.valuePropsLabel', { + return i18n.translate('data.search.aggs.metrics.percentiles.valuePropsLabel', { defaultMessage: '{percentile} percentile of {label}', values: { percentile: ordinalSuffix(this.key), label }, }); @@ -45,11 +45,11 @@ const valueProps = { export const percentilesMetricAgg = new MetricAggType({ name: METRIC_TYPES.PERCENTILES, - title: i18n.translate('common.ui.aggTypes.metrics.percentilesTitle', { + title: i18n.translate('data.search.aggs.metrics.percentilesTitle', { defaultMessage: 'Percentiles', }), makeLabel(agg) { - return i18n.translate('common.ui.aggTypes.metrics.percentilesLabel', { + return i18n.translate('data.search.aggs.metrics.percentilesLabel', { defaultMessage: 'Percentiles of {field}', values: { field: agg.getFieldDisplayName() }, }); diff --git a/src/legacy/ui/public/agg_types/metrics/percentiles_get_value.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/percentiles_get_value.ts similarity index 100% rename from src/legacy/ui/public/agg_types/metrics/percentiles_get_value.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/percentiles_get_value.ts diff --git a/src/legacy/ui/public/agg_types/metrics/serial_diff.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/serial_diff.ts similarity index 89% rename from src/legacy/ui/public/agg_types/metrics/serial_diff.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/serial_diff.ts index bb5431fbbefd9..5af6e1952d135 100644 --- a/src/legacy/ui/public/agg_types/metrics/serial_diff.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/serial_diff.ts @@ -23,11 +23,11 @@ import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; -const serialDiffTitle = i18n.translate('common.ui.aggTypes.metrics.serialDiffTitle', { +const serialDiffTitle = i18n.translate('data.search.aggs.metrics.serialDiffTitle', { defaultMessage: 'Serial Diff', }); -const serialDiffLabel = i18n.translate('common.ui.aggTypes.metrics.serialDiffLabel', { +const serialDiffLabel = i18n.translate('data.search.aggs.metrics.serialDiffLabel', { defaultMessage: 'serial diff', }); diff --git a/src/legacy/ui/public/agg_types/metrics/sibling_pipeline.test.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/sibling_pipeline.test.ts similarity index 100% rename from src/legacy/ui/public/agg_types/metrics/sibling_pipeline.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/sibling_pipeline.test.ts diff --git a/src/legacy/ui/public/agg_types/metrics/std_deviation.test.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/std_deviation.test.ts similarity index 100% rename from src/legacy/ui/public/agg_types/metrics/std_deviation.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/std_deviation.test.ts diff --git a/src/legacy/ui/public/agg_types/metrics/std_deviation.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/std_deviation.ts similarity index 85% rename from src/legacy/ui/public/agg_types/metrics/std_deviation.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/std_deviation.ts index b2e6d3b3ca4d0..caf3bb71dd89a 100644 --- a/src/legacy/ui/public/agg_types/metrics/std_deviation.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/std_deviation.ts @@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_response_agg_config_class'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; interface ValProp { valProp: string[]; @@ -51,7 +51,7 @@ const responseAggConfigProps = { keyedDetails(this: IStdDevAggConfig, customLabel: string, fieldDisplayName: string) { const label = customLabel || - i18n.translate('common.ui.aggTypes.metrics.standardDeviation.keyDetailsLabel', { + i18n.translate('data.search.aggs.metrics.standardDeviation.keyDetailsLabel', { defaultMessage: 'Standard Deviation of {fieldDisplayName}', values: { fieldDisplayName }, }); @@ -59,14 +59,14 @@ const responseAggConfigProps = { return { std_lower: { valProp: ['std_deviation_bounds', 'lower'], - title: i18n.translate('common.ui.aggTypes.metrics.standardDeviation.lowerKeyDetailsTitle', { + title: i18n.translate('data.search.aggs.metrics.standardDeviation.lowerKeyDetailsTitle', { defaultMessage: 'Lower {label}', values: { label }, }), }, std_upper: { valProp: ['std_deviation_bounds', 'upper'], - title: i18n.translate('common.ui.aggTypes.metrics.standardDeviation.upperKeyDetailsTitle', { + title: i18n.translate('data.search.aggs.metrics.standardDeviation.upperKeyDetailsTitle', { defaultMessage: 'Upper {label}', values: { label }, }), @@ -78,11 +78,11 @@ const responseAggConfigProps = { export const stdDeviationMetricAgg = new MetricAggType({ name: METRIC_TYPES.STD_DEV, dslName: 'extended_stats', - title: i18n.translate('common.ui.aggTypes.metrics.standardDeviationTitle', { + title: i18n.translate('data.search.aggs.metrics.standardDeviationTitle', { defaultMessage: 'Standard Deviation', }), makeLabel(agg) { - return i18n.translate('common.ui.aggTypes.metrics.standardDeviationLabel', { + return i18n.translate('data.search.aggs.metrics.standardDeviationLabel', { defaultMessage: 'Standard Deviation of {field}', values: { field: agg.getFieldDisplayName() }, }); diff --git a/src/legacy/ui/public/agg_types/metrics/sum.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/sum.ts similarity index 86% rename from src/legacy/ui/public/agg_types/metrics/sum.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/sum.ts index ce79c761ce799..f3450ba1700c8 100644 --- a/src/legacy/ui/public/agg_types/metrics/sum.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/sum.ts @@ -20,9 +20,9 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; -const sumTitle = i18n.translate('common.ui.aggTypes.metrics.sumTitle', { +const sumTitle = i18n.translate('data.search.aggs.metrics.sumTitle', { defaultMessage: 'Sum', }); @@ -30,7 +30,7 @@ export const sumMetricAgg = new MetricAggType({ name: METRIC_TYPES.SUM, title: sumTitle, makeLabel(aggConfig) { - return i18n.translate('common.ui.aggTypes.metrics.sumLabel', { + return i18n.translate('data.search.aggs.metrics.sumLabel', { defaultMessage: 'Sum of {field}', values: { field: aggConfig.getFieldDisplayName() }, }); diff --git a/src/legacy/ui/public/agg_types/metrics/top_hit.test.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/top_hit.test.ts similarity index 99% rename from src/legacy/ui/public/agg_types/metrics/top_hit.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/top_hit.test.ts index 3e861c052d367..a973de4fe8659 100644 --- a/src/legacy/ui/public/agg_types/metrics/top_hit.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/top_hit.test.ts @@ -21,7 +21,7 @@ import { dropRight, last } from 'lodash'; import { topHitMetricAgg } from './top_hit'; import { AggConfigs } from '../agg_configs'; import { IMetricAggConfig } from './metric_agg_type'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; jest.mock('ui/new_platform'); diff --git a/src/legacy/ui/public/agg_types/metrics/top_hit.ts b/src/legacy/core_plugins/data/public/search/aggs/metrics/top_hit.ts similarity index 82% rename from src/legacy/ui/public/agg_types/metrics/top_hit.ts rename to src/legacy/core_plugins/data/public/search/aggs/metrics/top_hit.ts index 43fe33bdebeb9..3112d882bb87e 100644 --- a/src/legacy/ui/public/agg_types/metrics/top_hit.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/metrics/top_hit.ts @@ -20,9 +20,8 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import { IMetricAggConfig, MetricAggType } from './metric_agg_type'; -import { aggTypeFieldFilters } from '../param_types/filter'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; // @ts-ignore import { wrapWithInlineComp } from '../buckets/inline_comp_wrapper'; @@ -33,27 +32,16 @@ const isNumericFieldSelected = (agg: IMetricAggConfig) => { return field && field.type && field.type === KBN_FIELD_TYPES.NUMBER; }; -aggTypeFieldFilters.addFilter((field, aggConfig) => { - if ( - aggConfig.type.name !== METRIC_TYPES.TOP_HITS || - _.get(aggConfig.schema, 'aggSettings.top_hits.allowStrings', false) - ) { - return true; - } - - return field.type === KBN_FIELD_TYPES.NUMBER; -}); - export const topHitMetricAgg = new MetricAggType({ name: METRIC_TYPES.TOP_HITS, - title: i18n.translate('common.ui.aggTypes.metrics.topHitTitle', { + title: i18n.translate('data.search.aggs.metrics.topHitTitle', { defaultMessage: 'Top Hit', }), makeLabel(aggConfig) { - const lastPrefixLabel = i18n.translate('common.ui.aggTypes.metrics.topHit.lastPrefixLabel', { + const lastPrefixLabel = i18n.translate('data.search.aggs.metrics.topHit.lastPrefixLabel', { defaultMessage: 'Last', }); - const firstPrefixLabel = i18n.translate('common.ui.aggTypes.metrics.topHit.firstPrefixLabel', { + const firstPrefixLabel = i18n.translate('data.search.aggs.metrics.topHit.firstPrefixLabel', { defaultMessage: 'First', }); @@ -75,7 +63,10 @@ export const topHitMetricAgg = new MetricAggType({ name: 'field', type: 'field', onlyAggregatable: false, - filterFieldTypes: '*', + filterFieldTypes: (aggConfig: IMetricAggConfig) => + _.get(aggConfig.schema, 'aggSettings.top_hits.allowStrings', false) + ? '*' + : KBN_FIELD_TYPES.NUMBER, write(agg, output) { const field = agg.getParam('field'); output.params = {}; @@ -106,7 +97,7 @@ export const topHitMetricAgg = new MetricAggType({ type: 'optioned', options: [ { - text: i18n.translate('common.ui.aggTypes.metrics.topHit.minLabel', { + text: i18n.translate('data.search.aggs.metrics.topHit.minLabel', { defaultMessage: 'Min', }), isCompatible: isNumericFieldSelected, @@ -114,7 +105,7 @@ export const topHitMetricAgg = new MetricAggType({ value: 'min', }, { - text: i18n.translate('common.ui.aggTypes.metrics.topHit.maxLabel', { + text: i18n.translate('data.search.aggs.metrics.topHit.maxLabel', { defaultMessage: 'Max', }), isCompatible: isNumericFieldSelected, @@ -122,7 +113,7 @@ export const topHitMetricAgg = new MetricAggType({ value: 'max', }, { - text: i18n.translate('common.ui.aggTypes.metrics.topHit.sumLabel', { + text: i18n.translate('data.search.aggs.metrics.topHit.sumLabel', { defaultMessage: 'Sum', }), isCompatible: isNumericFieldSelected, @@ -130,7 +121,7 @@ export const topHitMetricAgg = new MetricAggType({ value: 'sum', }, { - text: i18n.translate('common.ui.aggTypes.metrics.topHit.averageLabel', { + text: i18n.translate('data.search.aggs.metrics.topHit.averageLabel', { defaultMessage: 'Average', }), isCompatible: isNumericFieldSelected, @@ -138,7 +129,7 @@ export const topHitMetricAgg = new MetricAggType({ value: 'average', }, { - text: i18n.translate('common.ui.aggTypes.metrics.topHit.concatenateLabel', { + text: i18n.translate('data.search.aggs.metrics.topHit.concatenateLabel', { defaultMessage: 'Concatenate', }), isCompatible(aggConfig: IMetricAggConfig) { @@ -174,13 +165,13 @@ export const topHitMetricAgg = new MetricAggType({ default: 'desc', options: [ { - text: i18n.translate('common.ui.aggTypes.metrics.topHit.descendingLabel', { + text: i18n.translate('data.search.aggs.metrics.topHit.descendingLabel', { defaultMessage: 'Descending', }), value: 'desc', }, { - text: i18n.translate('common.ui.aggTypes.metrics.topHit.ascendingLabel', { + text: i18n.translate('data.search.aggs.metrics.topHit.ascendingLabel', { defaultMessage: 'Ascending', }), value: 'asc', diff --git a/src/legacy/ui/public/agg_types/param_types/agg.ts b/src/legacy/core_plugins/data/public/search/aggs/param_types/agg.ts similarity index 100% rename from src/legacy/ui/public/agg_types/param_types/agg.ts rename to src/legacy/core_plugins/data/public/search/aggs/param_types/agg.ts diff --git a/src/legacy/ui/public/agg_types/param_types/base.ts b/src/legacy/core_plugins/data/public/search/aggs/param_types/base.ts similarity index 94% rename from src/legacy/ui/public/agg_types/param_types/base.ts rename to src/legacy/core_plugins/data/public/search/aggs/param_types/base.ts index 15ec44e2ca5ae..1523cb03eb966 100644 --- a/src/legacy/ui/public/agg_types/param_types/base.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/param_types/base.ts @@ -17,9 +17,9 @@ * under the License. */ -import { AggConfigs } from '../agg_configs'; +import { IAggConfigs } from '../agg_configs'; import { AggConfig } from '../agg_config'; -import { FetchOptions, ISearchSource } from '../../../../../plugins/data/public'; +import { FetchOptions, ISearchSource } from '../../../../../../../plugins/data/public'; export class BaseParamType { name: string; @@ -31,7 +31,7 @@ export class BaseParamType { write: ( aggConfig: TAggConfig, output: Record, - aggConfigs?: AggConfigs, + aggConfigs?: IAggConfigs, locals?: Record ) => void; serialize: (value: any, aggConfig?: TAggConfig) => any; diff --git a/src/legacy/ui/public/agg_types/param_types/field.test.ts b/src/legacy/core_plugins/data/public/search/aggs/param_types/field.test.ts similarity index 54% rename from src/legacy/ui/public/agg_types/param_types/field.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/param_types/field.test.ts index 9cea2934d7459..fa88754ac60b9 100644 --- a/src/legacy/ui/public/agg_types/param_types/field.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/param_types/field.test.ts @@ -17,9 +17,13 @@ * under the License. */ +import { get } from 'lodash'; import { BaseParamType } from './base'; import { FieldParamType } from './field'; -import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; +import { IAggConfig } from '../agg_config'; +import { IMetricAggConfig } from '../metrics/metric_agg_type'; +import { Schema } from '../schemas'; jest.mock('ui/new_platform'); @@ -45,7 +49,11 @@ describe('Field', () => { searchable: true, }, ], - } as any; + }; + + const agg = ({ + getIndexPattern: jest.fn(() => indexPattern), + } as unknown) as IAggConfig; describe('constructor', () => { it('it is an instance of BaseParamType', () => { @@ -65,7 +73,7 @@ describe('Field', () => { type: 'field', }); - const fields = aggParam.getAvailableFields(indexPattern.fields); + const fields = aggParam.getAvailableFields(agg); expect(fields.length).toBe(1); @@ -82,7 +90,58 @@ describe('Field', () => { aggParam.onlyAggregatable = false; - const fields = aggParam.getAvailableFields(indexPattern.fields); + const fields = aggParam.getAvailableFields(agg); + + expect(fields.length).toBe(2); + }); + + it('should return all fields if filterFieldTypes was not specified', () => { + const aggParam = new FieldParamType({ + name: 'field', + type: 'field', + }); + + indexPattern.fields[1].aggregatable = true; + + const fields = aggParam.getAvailableFields(agg); + + expect(fields.length).toBe(2); + }); + + it('should return only numeric fields if filterFieldTypes was specified as a function', () => { + const aggParam = new FieldParamType({ + name: 'field', + type: 'field', + filterFieldTypes: (aggConfig: IMetricAggConfig) => + get(aggConfig.schema, 'aggSettings.top_hits.allowStrings', false) + ? '*' + : KBN_FIELD_TYPES.NUMBER, + }); + const fields = aggParam.getAvailableFields(agg); + + expect(fields.length).toBe(1); + expect(fields[0].type).toBe(KBN_FIELD_TYPES.NUMBER); + }); + + it('should return all fields if filterFieldTypes was specified as a function and aggSettings allow string type fields', () => { + const aggParam = new FieldParamType({ + name: 'field', + type: 'field', + filterFieldTypes: (aggConfig: IMetricAggConfig) => + get(aggConfig.schema, 'aggSettings.top_hits.allowStrings', false) + ? '*' + : KBN_FIELD_TYPES.NUMBER, + }); + + agg.schema = { + aggSettings: { + top_hits: { + allowStrings: true, + }, + }, + } as Schema; + + const fields = aggParam.getAvailableFields(agg); expect(fields.length).toBe(2); }); diff --git a/src/legacy/ui/public/agg_types/param_types/field.ts b/src/legacy/core_plugins/data/public/search/aggs/param_types/field.ts similarity index 65% rename from src/legacy/ui/public/agg_types/param_types/field.ts rename to src/legacy/core_plugins/data/public/search/aggs/param_types/field.ts index d01e059c6c616..9a204bb151e2d 100644 --- a/src/legacy/ui/public/agg_types/param_types/field.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/param_types/field.ts @@ -17,22 +17,27 @@ * under the License. */ -// @ts-ignore import { i18n } from '@kbn/i18n'; -import { AggConfig } from '../agg_config'; -import { SavedObjectNotFound } from '../../../../../plugins/kibana_utils/public'; +import { isFunction } from 'lodash'; +import { npStart } from 'ui/new_platform'; +import { IAggConfig } from '../agg_config'; +import { SavedObjectNotFound } from '../../../../../../../plugins/kibana_utils/public'; import { BaseParamType } from './base'; -import { npStart } from '../../new_platform'; import { propFilter } from '../filter'; -import { Field, IFieldList } from '../../../../../plugins/data/public'; -import { isNestedField } from '../../../../../plugins/data/public'; +import { IMetricAggConfig } from '../metrics/metric_agg_type'; +import { Field, isNestedField, KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; const filterByType = propFilter('type'); +type FieldTypes = KBN_FIELD_TYPES | KBN_FIELD_TYPES[] | '*'; +export type FilterFieldTypes = ((aggConfig: IMetricAggConfig) => FieldTypes) | FieldTypes; +// TODO need to make a more explicit interface for this +export type IFieldParamType = FieldParamType; + export class FieldParamType extends BaseParamType { required = true; scriptable = true; - filterFieldTypes: string; + filterFieldTypes: FilterFieldTypes; onlyAggregatable: boolean; constructor(config: Record) { @@ -42,20 +47,17 @@ export class FieldParamType extends BaseParamType { this.onlyAggregatable = config.onlyAggregatable !== false; if (!config.write) { - this.write = (aggConfig: AggConfig, output: Record) => { + this.write = (aggConfig: IAggConfig, output: Record) => { const field = aggConfig.getField(); if (!field) { throw new TypeError( - i18n.translate( - 'common.ui.aggTypes.paramTypes.field.requiredFieldParameterErrorMessage', - { - defaultMessage: '{fieldParameter} is a required parameter', - values: { - fieldParameter: '"field"', - }, - } - ) + i18n.translate('data.search.aggs.paramTypes.field.requiredFieldParameterErrorMessage', { + defaultMessage: '{fieldParameter} is a required parameter', + values: { + fieldParameter: '"field"', + }, + }) ); } @@ -74,7 +76,7 @@ export class FieldParamType extends BaseParamType { return field.name; }; - this.deserialize = (fieldName: string, aggConfig?: AggConfig) => { + this.deserialize = (fieldName: string, aggConfig?: IAggConfig) => { if (!aggConfig) { throw new Error('aggConfig was not provided to FieldParamType deserialize function'); } @@ -85,13 +87,11 @@ export class FieldParamType extends BaseParamType { } // @ts-ignore - const validField = this.getAvailableFields(aggConfig.getIndexPattern().fields).find( - (f: any) => f.name === fieldName - ); + const validField = this.getAvailableFields(aggConfig).find((f: any) => f.name === fieldName); if (!validField) { npStart.core.notifications.toasts.addDanger( i18n.translate( - 'common.ui.aggTypes.paramTypes.field.invalidSavedFieldParameterErrorMessage', + 'data.search.aggs.paramTypes.field.invalidSavedFieldParameterErrorMessage', { defaultMessage: 'Saved {fieldParameter} parameter is now invalid. Please select a new field.', @@ -110,7 +110,8 @@ export class FieldParamType extends BaseParamType { /** * filter the fields to the available ones */ - getAvailableFields = (fields: IFieldList) => { + getAvailableFields = (aggConfig: IAggConfig) => { + const fields = aggConfig.getIndexPattern().fields; const filteredFields = fields.filter((field: Field) => { const { onlyAggregatable, scriptable, filterFieldTypes } = this; @@ -121,8 +122,10 @@ export class FieldParamType extends BaseParamType { return false; } - if (!filterFieldTypes) { - return true; + if (isFunction(filterFieldTypes)) { + const filter = filterFieldTypes(aggConfig as IMetricAggConfig); + + return filterByType([field], filter).length !== 0; } return filterByType([field], filterFieldTypes).length !== 0; diff --git a/src/legacy/ui/public/agg_types/param_types/filter/field_filters.test.ts b/src/legacy/core_plugins/data/public/search/aggs/param_types/filter/field_filters.test.ts similarity index 97% rename from src/legacy/ui/public/agg_types/param_types/filter/field_filters.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/param_types/filter/field_filters.test.ts index 384c142408012..fb53e72b85c60 100644 --- a/src/legacy/ui/public/agg_types/param_types/filter/field_filters.test.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/param_types/filter/field_filters.test.ts @@ -20,7 +20,7 @@ import { IndexedArray } from 'ui/indexed_array'; import { AggTypeFieldFilters } from './field_filters'; import { AggConfig } from '../../agg_config'; -import { Field } from '../../../../../../plugins/data/public'; +import { Field } from '../../../../../../../../plugins/data/public'; describe('AggTypeFieldFilters', () => { let registry: AggTypeFieldFilters; diff --git a/src/legacy/ui/public/agg_types/param_types/filter/field_filters.ts b/src/legacy/core_plugins/data/public/search/aggs/param_types/filter/field_filters.ts similarity index 100% rename from src/legacy/ui/public/agg_types/param_types/filter/field_filters.ts rename to src/legacy/core_plugins/data/public/search/aggs/param_types/filter/field_filters.ts diff --git a/src/legacy/ui/public/agg_types/param_types/filter/index.ts b/src/legacy/core_plugins/data/public/search/aggs/param_types/filter/index.ts similarity index 100% rename from src/legacy/ui/public/agg_types/param_types/filter/index.ts rename to src/legacy/core_plugins/data/public/search/aggs/param_types/filter/index.ts diff --git a/src/legacy/ui/public/agg_types/param_types/index.ts b/src/legacy/core_plugins/data/public/search/aggs/param_types/index.ts similarity index 100% rename from src/legacy/ui/public/agg_types/param_types/index.ts rename to src/legacy/core_plugins/data/public/search/aggs/param_types/index.ts diff --git a/src/legacy/ui/public/agg_types/param_types/json.test.ts b/src/legacy/core_plugins/data/public/search/aggs/param_types/json.test.ts similarity index 100% rename from src/legacy/ui/public/agg_types/param_types/json.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/param_types/json.test.ts diff --git a/src/legacy/ui/public/agg_types/param_types/json.ts b/src/legacy/core_plugins/data/public/search/aggs/param_types/json.ts similarity index 100% rename from src/legacy/ui/public/agg_types/param_types/json.ts rename to src/legacy/core_plugins/data/public/search/aggs/param_types/json.ts diff --git a/src/legacy/ui/public/agg_types/param_types/optioned.test.ts b/src/legacy/core_plugins/data/public/search/aggs/param_types/optioned.test.ts similarity index 100% rename from src/legacy/ui/public/agg_types/param_types/optioned.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/param_types/optioned.test.ts diff --git a/src/legacy/ui/public/agg_types/param_types/optioned.ts b/src/legacy/core_plugins/data/public/search/aggs/param_types/optioned.ts similarity index 100% rename from src/legacy/ui/public/agg_types/param_types/optioned.ts rename to src/legacy/core_plugins/data/public/search/aggs/param_types/optioned.ts diff --git a/src/legacy/ui/public/agg_types/param_types/string.test.ts b/src/legacy/core_plugins/data/public/search/aggs/param_types/string.test.ts similarity index 100% rename from src/legacy/ui/public/agg_types/param_types/string.test.ts rename to src/legacy/core_plugins/data/public/search/aggs/param_types/string.test.ts diff --git a/src/legacy/ui/public/agg_types/param_types/string.ts b/src/legacy/core_plugins/data/public/search/aggs/param_types/string.ts similarity index 100% rename from src/legacy/ui/public/agg_types/param_types/string.ts rename to src/legacy/core_plugins/data/public/search/aggs/param_types/string.ts diff --git a/src/legacy/ui/public/agg_types/schemas.ts b/src/legacy/core_plugins/data/public/search/aggs/schemas.ts similarity index 98% rename from src/legacy/ui/public/agg_types/schemas.ts rename to src/legacy/core_plugins/data/public/search/aggs/schemas.ts index 05723cac1869d..1aa5ebe08656b 100644 --- a/src/legacy/ui/public/agg_types/schemas.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/schemas.ts @@ -45,7 +45,7 @@ export interface Schema { aggSettings?: any; } -class Schemas { +export class Schemas { // @ts-ignore all: IndexedArray; @@ -103,5 +103,3 @@ class Schemas { .commit(); } } - -export { Schemas }; diff --git a/src/legacy/core_plugins/data/public/search/aggs/types.ts b/src/legacy/core_plugins/data/public/search/aggs/types.ts new file mode 100644 index 0000000000000..2c918abf99fca --- /dev/null +++ b/src/legacy/core_plugins/data/public/search/aggs/types.ts @@ -0,0 +1,29 @@ +/* + * 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 { IAggConfig } from './agg_config'; +export { IAggConfigs } from './agg_configs'; +export { IAggType } from './agg_type'; +export { AggParam, AggParamOption } from './agg_params'; +export { IFieldParamType } from './param_types'; +export { IMetricAggType } from './metrics/metric_agg_type'; +export { DateRangeKey } from './buckets/date_range'; +export { IpRangeKey } from './buckets/ip_range'; +export { OptionedValueProp, OptionedParamEditorProps } from './param_types/optioned'; +export { ISchemas } from './schemas'; diff --git a/src/legacy/ui/public/agg_types/utils.test.tsx b/src/legacy/core_plugins/data/public/search/aggs/utils.test.tsx similarity index 100% rename from src/legacy/ui/public/agg_types/utils.test.tsx rename to src/legacy/core_plugins/data/public/search/aggs/utils.test.tsx diff --git a/src/legacy/ui/public/agg_types/utils.ts b/src/legacy/core_plugins/data/public/search/aggs/utils.ts similarity index 92% rename from src/legacy/ui/public/agg_types/utils.ts rename to src/legacy/core_plugins/data/public/search/aggs/utils.ts index e382f821b31a9..62f07ce44ab46 100644 --- a/src/legacy/ui/public/agg_types/utils.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/utils.ts @@ -17,8 +17,8 @@ * under the License. */ -import { isValidEsInterval } from '../../../core_plugins/data/common/parse_es_interval/is_valid_es_interval'; -import { leastCommonInterval } from '../vis/lib/least_common_interval'; +import { leastCommonInterval } from 'ui/vis/lib/least_common_interval'; +import { isValidEsInterval } from '../../../common'; /** * Check a string if it's a valid JSON. diff --git a/src/legacy/core_plugins/data/public/search/expressions/esaggs.ts b/src/legacy/core_plugins/data/public/search/expressions/esaggs.ts index b4ea2cd378d61..43927337ce574 100644 --- a/src/legacy/core_plugins/data/public/search/expressions/esaggs.ts +++ b/src/legacy/core_plugins/data/public/search/expressions/esaggs.ts @@ -19,7 +19,7 @@ import { get, has } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { AggConfigs } from 'ui/agg_types/agg_configs'; +import { AggConfigs, IAggConfigs } from 'ui/agg_types'; import { createFormat } from 'ui/visualize/loader/pipeline_helpers/utilities'; import { KibanaContext, @@ -50,7 +50,7 @@ import { serializeAggConfig } from './utils'; export interface RequestHandlerParams { searchSource: ISearchSource; - aggs: AggConfigs; + aggs: IAggConfigs; timeRange?: TimeRange; query?: Query; filters?: esFilters.Filter[]; diff --git a/src/legacy/core_plugins/data/public/search/expressions/utils.ts b/src/legacy/core_plugins/data/public/search/expressions/utils.ts index 4f104f2569a98..f0958ba20820f 100644 --- a/src/legacy/core_plugins/data/public/search/expressions/utils.ts +++ b/src/legacy/core_plugins/data/public/search/expressions/utils.ts @@ -17,12 +17,12 @@ * under the License. */ -import { AggConfig } from 'ui/agg_types/agg_config'; -import { AggConfigs } from '../../../../../ui/public/agg_types/agg_configs'; +import { AggConfigs } from '../aggs'; +import { IAggConfig } from '../aggs/types'; import { KibanaDatatableColumnMeta } from '../../../../../../plugins/expressions/common/expression_types'; import { IndexPattern } from '../../../../../../plugins/data/public'; -export const serializeAggConfig = (aggConfig: AggConfig): KibanaDatatableColumnMeta => { +export const serializeAggConfig = (aggConfig: IAggConfig): KibanaDatatableColumnMeta => { return { type: aggConfig.type.name, indexPatternId: aggConfig.getIndexPattern().id, diff --git a/src/legacy/core_plugins/data/public/search/index.ts b/src/legacy/core_plugins/data/public/search/index.ts index c975d5772e0a8..90e191b769a8d 100644 --- a/src/legacy/core_plugins/data/public/search/index.ts +++ b/src/legacy/core_plugins/data/public/search/index.ts @@ -17,5 +17,6 @@ * under the License. */ +export * from './aggs'; export { getRequestInspectorStats, getResponseInspectorStats } from './utils'; export { serializeAggConfig } from './expressions/utils'; diff --git a/src/legacy/core_plugins/data/public/search/search_service.ts b/src/legacy/core_plugins/data/public/search/search_service.ts new file mode 100644 index 0000000000000..45f9ff17328ad --- /dev/null +++ b/src/legacy/core_plugins/data/public/search/search_service.ts @@ -0,0 +1,93 @@ +/* + * 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 { CoreSetup, CoreStart } from '../../../../../core/public'; +import { + aggTypes, + AggType, + AggConfig, + AggConfigs, + FieldParamType, + MetricAggType, + aggTypeFieldFilters, + setBounds, + parentPipelineAggHelper, + siblingPipelineAggHelper, +} from './aggs'; + +interface AggsSetup { + types: typeof aggTypes; +} + +interface AggsStart { + types: typeof aggTypes; + AggConfig: typeof AggConfig; + AggConfigs: typeof AggConfigs; + AggType: typeof AggType; + aggTypeFieldFilters: typeof aggTypeFieldFilters; + FieldParamType: typeof FieldParamType; + MetricAggType: typeof MetricAggType; + parentPipelineAggHelper: typeof parentPipelineAggHelper; + siblingPipelineAggHelper: typeof siblingPipelineAggHelper; + setBounds: typeof setBounds; +} + +export interface SearchSetup { + aggs: AggsSetup; +} + +export interface SearchStart { + aggs: AggsStart; +} + +/** + * The contract provided here is a new platform shim for ui/agg_types. + * + * Once it has been refactored to work with new platform services, + * it will move into the existing search service in src/plugins/data/public/search + */ +export class SearchService { + public setup(core: CoreSetup): SearchSetup { + return { + aggs: { + types: aggTypes, // TODO convert to registry + // TODO add other items as needed + }, + }; + } + + public start(core: CoreStart): SearchStart { + return { + aggs: { + types: aggTypes, // TODO convert to registry + AggConfig, // TODO make static + AggConfigs, + AggType, + aggTypeFieldFilters, + FieldParamType, + MetricAggType, + parentPipelineAggHelper, // TODO make static + siblingPipelineAggHelper, // TODO make static + setBounds, // TODO make static + }, + }; + } + + public stop() {} +} diff --git a/src/legacy/core_plugins/data/public/search/types.ts b/src/legacy/core_plugins/data/public/search/types.ts index 140ceea487099..47ea1d168f379 100644 --- a/src/legacy/core_plugins/data/public/search/types.ts +++ b/src/legacy/core_plugins/data/public/search/types.ts @@ -17,4 +17,5 @@ * under the License. */ +export * from './aggs/types'; export * from './utils/types'; diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index 8c35044b52c9e..395e0da218307 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -97,13 +97,8 @@ export default function(kibana) { }), order: -1001, url: `${kbnBaseUrl}#/dashboards`, - // The subUrlBase is the common substring of all urls for this app. If not given, it defaults to the url - // above. This app has to use a different subUrlBase, in addition to the url above, because "#/dashboard" - // routes to a page that creates a new dashboard. When we introduced a landing page, we needed to change - // the url above in order to preserve the original url for BWC. The subUrlBase helps the Chrome api nav - // to determine what url to use for the app link. - subUrlBase: `${kbnBaseUrl}#/dashboard`, euiIconType: 'dashboardApp', + disableSubUrlTracking: true, category: DEFAULT_APP_CATEGORIES.analyze, }, { diff --git a/src/legacy/core_plugins/kibana/public/.eslintrc.js b/src/legacy/core_plugins/kibana/public/.eslintrc.js index 9b45217287dc8..b3ee0a8fa7b04 100644 --- a/src/legacy/core_plugins/kibana/public/.eslintrc.js +++ b/src/legacy/core_plugins/kibana/public/.eslintrc.js @@ -17,8 +17,15 @@ * under the License. */ +const topLevelConfig = require('../../../../../.eslintrc.js'); const path = require('path'); +const topLevelRestricedZones = topLevelConfig.overrides.find( + override => + override.files[0] === '**/*.{js,ts,tsx}' && + Object.keys(override.rules)[0] === '@kbn/eslint/no-restricted-paths' +).rules['@kbn/eslint/no-restricted-paths'][1].zones; + /** * Builds custom restricted paths configuration for the shimmed plugins within the kibana plugin. * These custom rules extend the default checks in the top level `eslintrc.js` by also checking two other things: @@ -28,34 +35,37 @@ const path = require('path'); * @returns zones configuration for the no-restricted-paths linter */ function buildRestrictedPaths(shimmedPlugins) { - return shimmedPlugins.map(shimmedPlugin => ([{ - target: [ - `src/legacy/core_plugins/kibana/public/${shimmedPlugin}/np_ready/**/*`, - ], - from: [ - 'ui/**/*', - 'src/legacy/ui/**/*', - 'src/legacy/core_plugins/kibana/public/**/*', - 'src/legacy/core_plugins/data/public/**/*', - '!src/legacy/core_plugins/data/public/index.ts', - `!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/**/*`, - ], - allowSameFolder: false, - errorMessage: `${shimmedPlugin} is a shimmed plugin that is not allowed to import modules from the legacy platform. If you need legacy modules for the transition period, import them either in the legacy_imports, kibana_services or index module.`, - }, { - target: [ - 'src/**/*', - `!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/**/*`, - 'x-pack/**/*', - ], - from: [ - `src/legacy/core_plugins/kibana/public/${shimmedPlugin}/**/*`, - `!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/index.ts`, - `!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/legacy.ts`, - ], - allowSameFolder: false, - errorMessage: `kibana/public/${shimmedPlugin} is behaving like a NP plugin and does not allow deep imports. If you need something from within ${shimmedPlugin} in another plugin, consider re-exporting it from the top level index module`, - }])).reduce((acc, part) => [...acc, ...part], []); + return shimmedPlugins + .map(shimmedPlugin => [ + { + target: [`src/legacy/core_plugins/kibana/public/${shimmedPlugin}/np_ready/**/*`], + from: [ + 'ui/**/*', + 'src/legacy/ui/**/*', + 'src/legacy/core_plugins/kibana/public/**/*', + 'src/legacy/core_plugins/data/public/**/*', + '!src/legacy/core_plugins/data/public/index.ts', + `!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/**/*`, + ], + allowSameFolder: false, + errorMessage: `${shimmedPlugin} is a shimmed plugin that is not allowed to import modules from the legacy platform. If you need legacy modules for the transition period, import them either in the legacy_imports, kibana_services or index module.`, + }, + { + target: [ + 'src/**/*', + `!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/**/*`, + 'x-pack/**/*', + ], + from: [ + `src/legacy/core_plugins/kibana/public/${shimmedPlugin}/**/*`, + `!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/index.ts`, + `!src/legacy/core_plugins/kibana/public/${shimmedPlugin}/legacy.ts`, + ], + allowSameFolder: false, + errorMessage: `kibana/public/${shimmedPlugin} is behaving like a NP plugin and does not allow deep imports. If you need something from within ${shimmedPlugin} in another plugin, consider re-exporting it from the top level index module`, + }, + ]) + .reduce((acc, part) => [...acc, ...part], []); } module.exports = { @@ -66,7 +76,9 @@ module.exports = { 'error', { basePath: path.resolve(__dirname, '../../../../../'), - zones: buildRestrictedPaths(['visualize', 'discover', 'dashboard', 'devTools', 'home']), + zones: topLevelRestricedZones.concat( + buildRestrictedPaths(['visualize', 'discover', 'dashboard', 'devTools', 'home']) + ), }, ], }, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/legacy.ts b/src/legacy/core_plugins/kibana/public/dashboard/legacy.ts index acbc4c4b6c47f..ca2dc9d5fb4f5 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/legacy.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/legacy.ts @@ -41,6 +41,7 @@ async function getAngularDependencies(): Promise(() => ({})); + private stopUrlTracking: (() => void) | undefined = undefined; + public setup( core: CoreSetup, - { __LEGACY: { getAngularDependencies }, home, kibana_legacy }: DashboardPluginSetupDependencies + { + __LEGACY: { getAngularDependencies }, + home, + kibana_legacy, + npData, + }: DashboardPluginSetupDependencies ) { + const { querySyncStateContainer, stop: stopQuerySyncStateContainer } = getQueryStateContainer( + npData.query + ); + const { appMounted, appUnMounted, stop: stopUrlTracker } = createKbnUrlTracker({ + baseUrl: core.http.basePath.prepend('/app/kibana'), + defaultSubUrl: '#/dashboards', + storageKey: 'lastUrl:dashboard', + navLinkUpdater$: this.appStateUpdater, + toastNotifications: core.notifications.toasts, + stateParams: [ + { + kbnUrlKey: '_g', + stateUpdate$: querySyncStateContainer.state$, + }, + ], + }); + this.stopUrlTracking = () => { + stopQuerySyncStateContainer(); + stopUrlTracker(); + }; const app: App = { id: '', title: 'Dashboards', @@ -81,6 +119,7 @@ export class DashboardPlugin implements Plugin { if (this.startDependencies === null) { throw new Error('not started yet'); } + appMounted(); const { savedObjectsClient, embeddables, @@ -114,10 +153,20 @@ export class DashboardPlugin implements Plugin { localStorage: new Storage(localStorage), }; const { renderApp } = await import('./np_ready/application'); - return renderApp(params.element, params.appBasePath, deps); + const unmount = renderApp(params.element, params.appBasePath, deps); + return () => { + unmount(); + appUnMounted(); + }; }, }; - kibana_legacy.registerLegacyApp({ ...app, id: 'dashboard' }); + kibana_legacy.registerLegacyApp({ + ...app, + id: 'dashboard', + // only register the updater in once app, otherwise all updates would happen twice + updater$: this.appStateUpdater.asObservable(), + navLinkId: 'kibana:dashboard', + }); kibana_legacy.registerLegacyApp({ ...app, id: 'dashboards' }); home.featureCatalogue.register({ @@ -147,4 +196,10 @@ export class DashboardPlugin implements Plugin { share, }; } + + stop() { + if (this.stopUrlTracking) { + this.stopUrlTracking(); + } + } } diff --git a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts index d1e1dafe7c878..f698a2ee361e0 100644 --- a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts @@ -55,7 +55,7 @@ export { buildVislibDimensions } from '../../../visualizations/public'; export { callAfterBindingsWorkaround } from 'ui/compat'; export { getRequestInspectorStats, getResponseInspectorStats } from '../../../data/public'; // @ts-ignore -export { intervalOptions } from 'ui/agg_types/buckets/_interval_options'; +export { intervalOptions } from 'ui/agg_types'; // @ts-ignore export { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query'; export { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_save_modal'; diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/instruction_set.js b/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/instruction_set.js index 15bda33534185..631ef1d6e0e42 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/instruction_set.js +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/instruction_set.js @@ -22,7 +22,7 @@ import PropTypes from 'prop-types'; import { Instruction } from './instruction'; import { ParameterForm } from './parameter_form'; import { Content } from './content'; -import { getDisplayText } from '../../../../../../../../plugins/home/server/tutorials/instructions/instruction_variant'; +import { getDisplayText } from '../../../../../../../../plugins/home/public'; import { EuiTabs, EuiTab, diff --git a/src/legacy/core_plugins/kibana/public/local_application_service/local_application_service.ts b/src/legacy/core_plugins/kibana/public/local_application_service/local_application_service.ts index d52bec8304ff9..c84a3e1eacbd2 100644 --- a/src/legacy/core_plugins/kibana/public/local_application_service/local_application_service.ts +++ b/src/legacy/core_plugins/kibana/public/local_application_service/local_application_service.ts @@ -79,6 +79,17 @@ export class LocalApplicationService { })(); }, }); + + if (app.updater$) { + app.updater$.subscribe(updater => { + const updatedFields = updater(app); + if (updatedFields && updatedFields.activeUrl) { + npStart.core.chrome.navLinks.update(app.navLinkId || app.id, { + url: updatedFields.activeUrl, + }); + } + }); + } }); npStart.plugins.kibana_legacy.getForwards().forEach(({ legacyAppId, newAppId, keepPrefix }) => { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.js index c990efaf43547..e10f033ed8165 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.js @@ -279,13 +279,12 @@ export class StepIndexPattern extends Component { render() { const { isIncludingSystemIndices, allIndices } = this.props; - const { query, partialMatchedIndices, exactMatchedIndices } = this.state; + const { partialMatchedIndices, exactMatchedIndices } = this.state; const matchedIndices = getMatchedIndices( allIndices, partialMatchedIndices, exactMatchedIndices, - query, isIncludingSystemIndices ); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/constants/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/constants/index.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/constants/index.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/constants/index.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/api/get_indices.error.json b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/api/get_indices.error.json deleted file mode 100644 index acb9a9ecd0206..0000000000000 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/api/get_indices.error.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "statusCode": 400, - "error": "Bad Request" -} diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/api/get_indices.exception.json b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/api/get_indices.exception.json deleted file mode 100644 index 1406b06813637..0000000000000 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/api/get_indices.exception.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "body": { - "error": { - "root_cause": [ - { - "type": "index_not_found_exception", - "reason": "no such index", - "index_uuid": "_na_", - "resource.type": "index_or_alias", - "resource.id": "t", - "index": "t" - } - ], - "type": "transport_exception", - "reason": "unable to communicate with remote cluster [cluster_one]", - "caused_by": { - "type": "index_not_found_exception", - "reason": "no such index", - "index_uuid": "_na_", - "resource.type": "index_or_alias", - "resource.id": "t", - "index": "t" - } - } - }, - "status": 500 -} diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/api/get_indices.success.json b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/api/get_indices.success.json deleted file mode 100644 index 1b261243ca728..0000000000000 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/api/get_indices.success.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "hits": { - "total": 1, - "max_score": 0.0, - "hits": [] - }, - "aggregations": { - "indices": { - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0, - "buckets": [{ - "key": "1", - "doc_count": 1 - },{ - "key": "2", - "doc_count": 1 - }] - } - } -} diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/get_indices.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/get_indices.test.js deleted file mode 100644 index 924b0dc46d74d..0000000000000 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/get_indices.test.js +++ /dev/null @@ -1,117 +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 { getIndices } from '../get_indices'; -import successfulResponse from './api/get_indices.success.json'; -import errorResponse from './api/get_indices.error.json'; -import exceptionResponse from './api/get_indices.exception.json'; -const mockIndexPatternCreationType = { - getIndexPatternType: () => 'default', - getIndexPatternName: () => 'name', - checkIndicesForErrors: () => false, - getShowSystemIndices: () => false, - renderPrompt: () => {}, - getIndexPatternMappings: () => { - return {}; - }, - getIndexTags: () => { - return []; - }, -}; - -describe('getIndices', () => { - it('should work in a basic case', async () => { - const es = { - search: () => new Promise(resolve => resolve(successfulResponse)), - }; - - const result = await getIndices(es, mockIndexPatternCreationType, 'kibana', 1); - expect(result.length).toBe(2); - expect(result[0].name).toBe('1'); - expect(result[1].name).toBe('2'); - }); - - it('should ignore ccs query-all', async () => { - expect((await getIndices(null, mockIndexPatternCreationType, '*:')).length).toBe(0); - }); - - it('should ignore a single comma', async () => { - expect((await getIndices(null, mockIndexPatternCreationType, ',')).length).toBe(0); - expect((await getIndices(null, mockIndexPatternCreationType, ',*')).length).toBe(0); - expect((await getIndices(null, mockIndexPatternCreationType, ',foobar')).length).toBe(0); - }); - - it('should trim the input', async () => { - let index; - const es = { - search: jest.fn().mockImplementation(params => { - index = params.index; - }), - }; - - await getIndices(es, mockIndexPatternCreationType, 'kibana ', 1); - expect(index).toBe('kibana'); - }); - - it('should use the limit', async () => { - let limit; - const es = { - search: jest.fn().mockImplementation(params => { - limit = params.body.aggs.indices.terms.size; - }), - }; - - await getIndices(es, mockIndexPatternCreationType, 'kibana', 10); - expect(limit).toBe(10); - }); - - describe('errors', () => { - it('should handle errors gracefully', async () => { - const es = { - search: () => new Promise(resolve => resolve(errorResponse)), - }; - - const result = await getIndices(es, mockIndexPatternCreationType, 'kibana', 1); - expect(result.length).toBe(0); - }); - - it('should throw exceptions', async () => { - const es = { - search: () => { - throw new Error('Fail'); - }, - }; - - await expect(getIndices(es, mockIndexPatternCreationType, 'kibana', 1)).rejects.toThrow(); - }); - - it('should handle index_not_found_exception errors gracefully', async () => { - const es = { - search: () => new Promise((resolve, reject) => reject(exceptionResponse)), - }; - - const result = await getIndices(es, mockIndexPatternCreationType, 'kibana', 1); - expect(result.length).toBe(0); - }); - - it('should throw an exception if no limit is provided', async () => { - await expect(getIndices({}, mockIndexPatternCreationType, 'kibana')).rejects.toThrow(); - }); - }); -}); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/can_append_wildcard.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/can_append_wildcard.test.ts similarity index 89% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/can_append_wildcard.test.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/can_append_wildcard.test.ts index 055632bdd19e0..14139c2e08dc0 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/can_append_wildcard.test.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/can_append_wildcard.test.ts @@ -17,13 +17,9 @@ * under the License. */ -import { canAppendWildcard } from '../can_append_wildcard'; +import { canAppendWildcard } from './can_append_wildcard'; describe('canAppendWildcard', () => { - test('ignores no data', () => { - expect(canAppendWildcard({})).toBeFalsy(); - }); - test('ignores symbols', () => { expect(canAppendWildcard('%')).toBeFalsy(); }); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/can_append_wildcard.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/can_append_wildcard.ts similarity index 94% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/can_append_wildcard.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/can_append_wildcard.ts index b47e645730aef..e9c4f75e4313b 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/can_append_wildcard.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/can_append_wildcard.ts @@ -17,7 +17,7 @@ * under the License. */ -export const canAppendWildcard = keyPressed => { +export const canAppendWildcard = (keyPressed: string) => { // If it's not a letter, number or is something longer, reject it if (!keyPressed || !/[a-z0-9]/i.test(keyPressed) || keyPressed.length !== 1) { return false; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/contains_illegal_characters.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/contains_illegal_characters.ts similarity index 90% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/contains_illegal_characters.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/contains_illegal_characters.ts index 31485bb3daaa2..ca4fc8122903c 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/contains_illegal_characters.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/contains_illegal_characters.ts @@ -17,6 +17,6 @@ * under the License. */ -export function containsIllegalCharacters(pattern, illegalCharacters) { +export function containsIllegalCharacters(pattern: string, illegalCharacters: string[]) { return illegalCharacters.some(char => pattern.includes(char)); } diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/contains_invalid_characters.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/contains_invalid_characters.test.ts similarity index 93% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/contains_invalid_characters.test.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/contains_invalid_characters.test.ts index 05c4aba2571bd..640908d3db6d1 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/contains_invalid_characters.test.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/contains_invalid_characters.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { containsIllegalCharacters } from '../contains_illegal_characters'; +import { containsIllegalCharacters } from './contains_illegal_characters'; describe('containsIllegalCharacters', () => { it('returns true with illegal characters', () => { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/ensure_minimum_time.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/ensure_minimum_time.test.ts similarity index 96% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/ensure_minimum_time.test.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/ensure_minimum_time.test.ts index 99724cbf3a2a7..e5fcfe056923a 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/ensure_minimum_time.test.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/ensure_minimum_time.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { ensureMinimumTime } from '../ensure_minimum_time'; +import { ensureMinimumTime } from './ensure_minimum_time'; describe('ensureMinimumTime', () => { it('resolves single promise', async done => { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/ensure_minimum_time.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/ensure_minimum_time.ts similarity index 97% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/ensure_minimum_time.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/ensure_minimum_time.ts index 0a6d3fcfbbdf0..84852ece485eb 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/ensure_minimum_time.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/ensure_minimum_time.ts @@ -27,7 +27,7 @@ export const DEFAULT_MINIMUM_TIME_MS = 300; export async function ensureMinimumTime( - promiseOrPromises, + promiseOrPromises: Promise | Array>, minimumTimeMs = DEFAULT_MINIMUM_TIME_MS ) { let returnValue; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/extract_time_fields.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/extract_time_fields.test.ts similarity index 89% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/extract_time_fields.test.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/extract_time_fields.test.ts index ec420e19817c7..4cd28090420a7 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/extract_time_fields.test.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/extract_time_fields.test.ts @@ -17,11 +17,14 @@ * under the License. */ -import { extractTimeFields } from '../extract_time_fields'; +import { extractTimeFields } from './extract_time_fields'; describe('extractTimeFields', () => { it('should handle no date fields', () => { - const fields = [{ type: 'text' }, { type: 'text' }]; + const fields = [ + { type: 'text', name: 'name' }, + { type: 'text', name: 'name' }, + ]; expect(extractTimeFields(fields)).toEqual([ { display: `The indices which match this index pattern don't contain any time fields.` }, diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/extract_time_fields.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/extract_time_fields.ts similarity index 92% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/extract_time_fields.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/extract_time_fields.ts index 1a9deefb217f2..0b95ec0a120da 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/extract_time_fields.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/extract_time_fields.ts @@ -18,8 +18,9 @@ */ import { i18n } from '@kbn/i18n'; +import { IFieldType } from '../../../../../../../../../plugins/data/public'; -export function extractTimeFields(fields) { +export function extractTimeFields(fields: IFieldType[]) { const dateFields = fields.filter(field => field.type === 'date'); const label = i18n.translate('kbn.management.createIndexPattern.stepTime.noTimeFieldsLabel', { defaultMessage: "The indices which match this index pattern don't contain any time fields.", diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.test.ts b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.test.ts new file mode 100644 index 0000000000000..cd7c8278adcc7 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.test.ts @@ -0,0 +1,167 @@ +/* + * 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 { getIndices } from './get_indices'; +import { IndexPatternCreationConfig } from './../../../../../../../management/public'; +import { LegacyApiCaller } from '../../../../../../../../../plugins/data/public'; + +export const successfulResponse = { + hits: { + total: 1, + max_score: 0.0, + hits: [], + }, + aggregations: { + indices: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: '1', + doc_count: 1, + }, + { + key: '2', + doc_count: 1, + }, + ], + }, + }, +}; + +export const exceptionResponse = { + body: { + error: { + root_cause: [ + { + type: 'index_not_found_exception', + reason: 'no such index', + index_uuid: '_na_', + 'resource.type': 'index_or_alias', + 'resource.id': 't', + index: 't', + }, + ], + type: 'transport_exception', + reason: 'unable to communicate with remote cluster [cluster_one]', + caused_by: { + type: 'index_not_found_exception', + reason: 'no such index', + index_uuid: '_na_', + 'resource.type': 'index_or_alias', + 'resource.id': 't', + index: 't', + }, + }, + }, + status: 500, +}; + +export const errorResponse = { + statusCode: 400, + error: 'Bad Request', +}; + +const mockIndexPatternCreationType = new IndexPatternCreationConfig({ + type: 'default', + name: 'name', + showSystemIndices: false, + httpClient: {}, + isBeta: false, +}); + +function esClientFactory(search: (params: any) => any): LegacyApiCaller { + return { + search, + msearch: () => ({ + abort: () => {}, + ...new Promise(resolve => resolve({})), + }), + }; +} + +const es = esClientFactory(() => successfulResponse); + +describe('getIndices', () => { + it('should work in a basic case', async () => { + const result = await getIndices(es, mockIndexPatternCreationType, 'kibana', 1); + expect(result.length).toBe(2); + expect(result[0].name).toBe('1'); + expect(result[1].name).toBe('2'); + }); + + it('should ignore ccs query-all', async () => { + expect((await getIndices(es, mockIndexPatternCreationType, '*:', 10)).length).toBe(0); + }); + + it('should ignore a single comma', async () => { + expect((await getIndices(es, mockIndexPatternCreationType, ',', 10)).length).toBe(0); + expect((await getIndices(es, mockIndexPatternCreationType, ',*', 10)).length).toBe(0); + expect((await getIndices(es, mockIndexPatternCreationType, ',foobar', 10)).length).toBe(0); + }); + + it('should trim the input', async () => { + let index; + const esClient = esClientFactory( + jest.fn().mockImplementation(params => { + index = params.index; + }) + ); + + await getIndices(esClient, mockIndexPatternCreationType, 'kibana ', 1); + expect(index).toBe('kibana'); + }); + + it('should use the limit', async () => { + let limit; + const esClient = esClientFactory( + jest.fn().mockImplementation(params => { + limit = params.body.aggs.indices.terms.size; + }) + ); + await getIndices(esClient, mockIndexPatternCreationType, 'kibana', 10); + expect(limit).toBe(10); + }); + + describe('errors', () => { + it('should handle errors gracefully', async () => { + const esClient = esClientFactory(() => errorResponse); + const result = await getIndices(esClient, mockIndexPatternCreationType, 'kibana', 1); + expect(result.length).toBe(0); + }); + + it('should throw exceptions', async () => { + const esClient = esClientFactory(() => { + throw new Error('Fail'); + }); + + await expect( + getIndices(esClient, mockIndexPatternCreationType, 'kibana', 1) + ).rejects.toThrow(); + }); + + it('should handle index_not_found_exception errors gracefully', async () => { + const esClient = esClientFactory( + () => new Promise((resolve, reject) => reject(exceptionResponse)) + ); + const result = await getIndices(esClient, mockIndexPatternCreationType, 'kibana', 1); + expect(result.length).toBe(0); + }); + }); +}); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.ts similarity index 83% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.ts index 8159fff8220bd..3848c425e2d49 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.ts @@ -18,8 +18,16 @@ */ import { get, sortBy } from 'lodash'; +import { IndexPatternCreationConfig } from '../../../../../../../management/public'; +import { DataPublicPluginStart } from '../../../../../../../../../plugins/data/public'; +import { MatchedIndex } from '../types'; -export async function getIndices(es, indexPatternCreationType, rawPattern, limit) { +export async function getIndices( + es: DataPublicPluginStart['search']['__LEGACY']['esClient'], + indexPatternCreationType: IndexPatternCreationConfig, + rawPattern: string, + limit: number +): Promise { const pattern = rawPattern.trim(); // Searching for `*:` fails for CCS environments. The search request @@ -70,10 +78,10 @@ export async function getIndices(es, indexPatternCreationType, rawPattern, limit return sortBy( response.aggregations.indices.buckets - .map(bucket => { + .map((bucket: { key: string; doc_count: number }) => { return bucket.key; }) - .map(indexName => { + .map((indexName: string) => { return { name: indexName, tags: indexPatternCreationType.getIndexTags(indexName), diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/get_matched_indices.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_matched_indices.test.ts similarity index 53% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/get_matched_indices.test.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_matched_indices.test.ts index 625c128181ffe..7aba50a7ca12b 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/__jest__/get_matched_indices.test.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_matched_indices.test.ts @@ -17,24 +17,32 @@ * under the License. */ -import { getMatchedIndices } from '../get_matched_indices'; +import { getMatchedIndices } from './get_matched_indices'; -jest.mock('../../constants', () => ({ +jest.mock('./../constants', () => ({ MAX_NUMBER_OF_MATCHING_INDICES: 6, })); +const tags: string[] = []; const indices = [ - { name: 'kibana' }, - { name: 'es' }, - { name: 'logstash' }, - { name: 'packetbeat' }, - { name: 'metricbeat' }, - { name: '.kibana' }, + { name: 'kibana', tags }, + { name: 'es', tags }, + { name: 'logstash', tags }, + { name: 'packetbeat', tags }, + { name: 'metricbeat', tags }, + { name: '.kibana', tags }, ]; -const partialIndices = [{ name: 'kibana' }, { name: 'es' }, { name: '.kibana' }]; +const partialIndices = [ + { name: 'kibana', tags }, + { name: 'es', tags }, + { name: '.kibana', tags }, +]; -const exactIndices = [{ name: 'kibana' }, { name: '.kibana' }]; +const exactIndices = [ + { name: 'kibana', tags }, + { name: '.kibana', tags }, +]; describe('getMatchedIndices', () => { it('should return all indices', () => { @@ -43,26 +51,32 @@ describe('getMatchedIndices', () => { exactMatchedIndices, partialMatchedIndices, visibleIndices, - } = getMatchedIndices(indices, partialIndices, exactIndices, '*', true); + } = getMatchedIndices(indices, partialIndices, exactIndices, true); expect(allIndices).toEqual([ - { name: 'kibana' }, - { name: 'es' }, - { name: 'logstash' }, - { name: 'packetbeat' }, - { name: 'metricbeat' }, - { name: '.kibana' }, + { name: 'kibana', tags }, + { name: 'es', tags }, + { name: 'logstash', tags }, + { name: 'packetbeat', tags }, + { name: 'metricbeat', tags }, + { name: '.kibana', tags }, ]); - expect(exactMatchedIndices).toEqual([{ name: 'kibana' }, { name: '.kibana' }]); + expect(exactMatchedIndices).toEqual([ + { name: 'kibana', tags }, + { name: '.kibana', tags }, + ]); expect(partialMatchedIndices).toEqual([ - { name: 'kibana' }, - { name: 'es' }, - { name: '.kibana' }, + { name: 'kibana', tags }, + { name: 'es', tags }, + { name: '.kibana', tags }, ]); - expect(visibleIndices).toEqual([{ name: 'kibana' }, { name: '.kibana' }]); + expect(visibleIndices).toEqual([ + { name: 'kibana', tags }, + { name: '.kibana', tags }, + ]); }); it('should return all indices except for system indices', () => { @@ -71,31 +85,38 @@ describe('getMatchedIndices', () => { exactMatchedIndices, partialMatchedIndices, visibleIndices, - } = getMatchedIndices(indices, partialIndices, exactIndices, '*', false); + } = getMatchedIndices(indices, partialIndices, exactIndices, false); expect(allIndices).toEqual([ - { name: 'kibana' }, - { name: 'es' }, - { name: 'logstash' }, - { name: 'packetbeat' }, - { name: 'metricbeat' }, + { name: 'kibana', tags }, + { name: 'es', tags }, + { name: 'logstash', tags }, + { name: 'packetbeat', tags }, + { name: 'metricbeat', tags }, ]); - expect(exactMatchedIndices).toEqual([{ name: 'kibana' }]); + expect(exactMatchedIndices).toEqual([{ name: 'kibana', tags }]); - expect(partialMatchedIndices).toEqual([{ name: 'kibana' }, { name: 'es' }]); + expect(partialMatchedIndices).toEqual([ + { name: 'kibana', tags }, + { name: 'es', tags }, + ]); - expect(visibleIndices).toEqual([{ name: 'kibana' }]); + expect(visibleIndices).toEqual([{ name: 'kibana', tags }]); }); it('should return partial matches as visible if there are no exact', () => { - const { visibleIndices } = getMatchedIndices(indices, partialIndices, [], '*', true); + const { visibleIndices } = getMatchedIndices(indices, partialIndices, [], true); - expect(visibleIndices).toEqual([{ name: 'kibana' }, { name: 'es' }, { name: '.kibana' }]); + expect(visibleIndices).toEqual([ + { name: 'kibana', tags }, + { name: 'es', tags }, + { name: '.kibana', tags }, + ]); }); it('should return all indices as visible if there are no exact or partial', () => { - const { visibleIndices } = getMatchedIndices(indices, [], [], '*', true); + const { visibleIndices } = getMatchedIndices(indices, [], [], true); expect(visibleIndices).toEqual(indices); }); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_matched_indices.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_matched_indices.ts similarity index 86% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_matched_indices.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_matched_indices.ts index 19a829a83a2b2..cc3fd4075aa0e 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_matched_indices.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_matched_indices.ts @@ -19,19 +19,21 @@ import { MAX_NUMBER_OF_MATCHING_INDICES } from '../constants'; -function isSystemIndex(index) { +function isSystemIndex(index: string): boolean { if (index.startsWith('.')) { return true; } if (index.includes(':')) { - return index.split(':').reduce((isSystem, index) => isSystem || isSystemIndex(index), false); + return index + .split(':') + .reduce((isSystem: boolean, idx) => isSystem || isSystemIndex(idx), false); } return false; } -function filterSystemIndices(indices, isIncludingSystemIndices) { +function filterSystemIndices(indices: MatchedIndex[], isIncludingSystemIndices: boolean) { if (!indices) { return indices; } @@ -62,12 +64,14 @@ function filterSystemIndices(indices, isIncludingSystemIndices) { This is the result of searching against a query that already ends in `*`. We call this `exact` matches because ES is telling us exactly what it matches */ + +import { MatchedIndex } from '../types'; + export function getMatchedIndices( - unfilteredAllIndices, - unfilteredPartialMatchedIndices, - unfilteredExactMatchedIndices, - query, - isIncludingSystemIndices + unfilteredAllIndices: MatchedIndex[], + unfilteredPartialMatchedIndices: MatchedIndex[], + unfilteredExactMatchedIndices: MatchedIndex[], + isIncludingSystemIndices: boolean = false ) { const allIndices = filterSystemIndices(unfilteredAllIndices, isIncludingSystemIndices); const partialMatchedIndices = filterSystemIndices( diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/index.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/index.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/index.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/types.ts b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/types.ts new file mode 100644 index 0000000000000..93bb6920c6981 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/types.ts @@ -0,0 +1,23 @@ +/* + * 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 interface MatchedIndex { + name: string; + tags: string[]; +} diff --git a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/creation/config.ts b/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/creation/config.ts index 0598c88c80ba7..b68b2e40aad9e 100644 --- a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/creation/config.ts +++ b/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/creation/config.ts @@ -102,7 +102,7 @@ export class IndexPatternCreationConfig { return this.showSystemIndices; } - public getIndexTags() { + public getIndexTags(indexName: string) { return []; } diff --git a/src/legacy/core_plugins/telemetry/server/collectors/usage/telemetry_usage_collector.test.ts b/src/legacy/core_plugins/telemetry/server/collectors/usage/telemetry_usage_collector.test.ts index cf6059faf0c05..78685cd6becc8 100644 --- a/src/legacy/core_plugins/telemetry/server/collectors/usage/telemetry_usage_collector.test.ts +++ b/src/legacy/core_plugins/telemetry/server/collectors/usage/telemetry_usage_collector.test.ts @@ -136,7 +136,7 @@ describe('telemetry_usage_collector', () => { const collectorOptions = createTelemetryUsageCollector(usageCollector, server); expect(collectorOptions.type).toBe('static_telemetry'); - expect(await collectorOptions.fetch()).toEqual(expectedObject); + expect(await collectorOptions.fetch({} as any)).toEqual(expectedObject); // Sending any as the callCluster client because it's not needed in this collector but TS requires it when calling it. }); }); }); diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_kibana.js b/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_kibana.ts similarity index 62% rename from src/legacy/core_plugins/telemetry/server/telemetry_collection/get_kibana.js rename to src/legacy/core_plugins/telemetry/server/telemetry_collection/get_kibana.ts index e65606a83afc8..537d5a85911cd 100644 --- a/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_kibana.js +++ b/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_kibana.ts @@ -17,9 +17,27 @@ * under the License. */ -import { get, omit } from 'lodash'; +import { omit } from 'lodash'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { CallCluster } from 'src/legacy/core_plugins/elasticsearch'; -export function handleKibanaStats(server, response) { +export interface KibanaUsageStats { + kibana: { + index: string; + }; + kibana_stats: { + os: { + platform: string; + platformRelease: string; + distro?: string; + distroRelease?: string; + }; + }; + + [plugin: string]: any; +} + +export function handleKibanaStats(server: any, response?: KibanaUsageStats) { if (!response) { server.log( ['warning', 'telemetry', 'local-stats'], @@ -30,8 +48,17 @@ export function handleKibanaStats(server, response) { const { kibana, kibana_stats: kibanaStats, ...plugins } = response; - const platform = get(kibanaStats, 'os.platform', 'unknown'); - const platformRelease = get(kibanaStats, 'os.platformRelease', 'unknown'); + const os = { + platform: 'unknown', + platformRelease: 'unknown', + ...kibanaStats.os, + }; + const formattedOsStats = Object.entries(os).reduce((acc, [key, value]) => { + return { + ...acc, + [`${key}s`]: [{ [key]: value, count: 1 }], + }; + }, {}); const version = server .config() @@ -44,16 +71,16 @@ export function handleKibanaStats(server, response) { ...omit(kibana, 'index'), // discard index count: 1, indices: 1, - os: { - platforms: [{ platform, count: 1 }], - platformReleases: [{ platformRelease, count: 1 }], - }, + os: formattedOsStats, versions: [{ version, count: 1 }], plugins, }; } -export async function getKibana(usageCollection, callWithInternalUser) { +export async function getKibana( + usageCollection: UsageCollectionSetup, + callWithInternalUser: CallCluster +): Promise { const usage = await usageCollection.bulkFetch(callWithInternalUser); return usageCollection.toObject(usage); } diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_stats.ts b/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_stats.ts index a4ea2eb534226..8adb6d237bee8 100644 --- a/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_stats.ts +++ b/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_stats.ts @@ -22,18 +22,25 @@ import { get, omit } from 'lodash'; import { getClusterInfo } from './get_cluster_info'; import { getClusterStats } from './get_cluster_stats'; // @ts-ignore -import { getKibana, handleKibanaStats } from './get_kibana'; +import { getKibana, handleKibanaStats, KibanaUsageStats } from './get_kibana'; import { StatsGetter } from '../collection_manager'; /** * Handle the separate local calls by combining them into a single object response that looks like the * "cluster_stats" document from X-Pack monitoring. * + * @param {Object} server ?? * @param {Object} clusterInfo Cluster info (GET /) * @param {Object} clusterStats Cluster stats (GET /_cluster/stats) + * @param {Object} kibana The Kibana Usage stats * @return {Object} A combined object containing the different responses. */ -export function handleLocalStats(server: any, clusterInfo: any, clusterStats: any, kibana: any) { +export function handleLocalStats( + server: any, + clusterInfo: any, + clusterStats: any, + kibana: KibanaUsageStats +) { return { timestamp: new Date().toISOString(), cluster_uuid: get(clusterInfo, 'cluster_uuid'), diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg.test.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/agg.test.tsx index 81c866923232e..f5ce55e82967d 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg.test.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg.test.tsx @@ -24,7 +24,7 @@ import { act } from 'react-dom/test-utils'; import { IndexPattern } from 'src/plugins/data/public'; import { VisState } from 'src/legacy/core_plugins/visualizations/public'; -import { AggType, AggGroupNames } from '../legacy_imports'; +import { IAggType, AggGroupNames } from '../legacy_imports'; import { DefaultEditorAgg, DefaultEditorAggProps } from './agg'; import { DefaultEditorAggParams } from './agg_params'; import { AGGS_ACTION_KEYS } from './agg_group_state'; @@ -117,7 +117,7 @@ describe('DefaultEditorAgg component', () => { (defaultProps.agg as any).brandNew = false; defaultProps.agg.type = { makeLabel: () => 'Agg description', - } as AggType; + } as IAggType; const comp = mount(); act(() => { @@ -258,11 +258,11 @@ describe('DefaultEditorAgg component', () => { it('should disable min_doc_count when agg is histogram or date_histogram', () => { defaultProps.agg.type = { name: 'histogram', - } as AggType; + } as IAggType; const compHistogram = shallow(); defaultProps.agg.type = { name: 'date_histogram', - } as AggType; + } as IAggType; const compDateHistogram = shallow(); expect(compHistogram.find(DefaultEditorAggParams).props()).toHaveProperty('disabledParams', [ @@ -276,7 +276,7 @@ describe('DefaultEditorAgg component', () => { it('should set error when agg is not histogram or date_histogram', () => { defaultProps.agg.type = { name: 'aggType', - } as AggType; + } as IAggType; const comp = shallow(); expect(comp.find(DefaultEditorAggParams).prop('aggError')).toBeDefined(); @@ -285,7 +285,7 @@ describe('DefaultEditorAgg component', () => { it('should set min_doc_count to true when agg type was changed to histogram', () => { defaultProps.agg.type = { name: 'aggType', - } as AggType; + } as IAggType; const comp = mount(); comp.setProps({ agg: { ...defaultProps.agg, type: { name: 'histogram' } } }); @@ -299,7 +299,7 @@ describe('DefaultEditorAgg component', () => { it('should set min_doc_count to 0 when agg type was changed to date_histogram', () => { defaultProps.agg.type = { name: 'aggType', - } as AggType; + } as IAggType; const comp = mount(); comp.setProps({ agg: { ...defaultProps.agg, type: { name: 'date_histogram' } } }); diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/agg.tsx index 871bd0cdf6811..5450c29450bac 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg.tsx @@ -28,7 +28,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { AggConfig } from '../legacy_imports'; +import { IAggConfig } from '../legacy_imports'; import { DefaultEditorAggParams } from './agg_params'; import { DefaultEditorAggCommonProps } from './agg_common_props'; import { AGGS_ACTION_KEYS, AggsAction } from './agg_group_state'; @@ -36,7 +36,7 @@ import { RowsOrColumnsControl } from './controls/rows_or_columns'; import { RadiusRatioOptionControl } from './controls/radius_ratio_option'; export interface DefaultEditorAggProps extends DefaultEditorAggCommonProps { - agg: AggConfig; + agg: IAggConfig; aggIndex: number; aggIsTooLow: boolean; dragHandleProps: {} | null; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_add.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/agg_add.tsx index f5175126c31c1..d8df5b315fca0 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_add.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_add.tsx @@ -29,10 +29,10 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { AggConfig, AggGroupNames, Schema } from '../legacy_imports'; +import { IAggConfig, AggGroupNames, Schema } from '../legacy_imports'; interface DefaultEditorAggAddProps { - group?: AggConfig[]; + group?: IAggConfig[]; groupName: string; schemas: Schema[]; stats: { diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_common_props.ts b/src/legacy/core_plugins/vis_default_editor/public/components/agg_common_props.ts index 8d803810b647a..17d2c18d2532c 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_common_props.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_common_props.ts @@ -18,25 +18,25 @@ */ import { VisState, VisParams } from 'src/legacy/core_plugins/visualizations/public'; -import { AggType, AggConfig, AggGroupNames, Schema } from '../legacy_imports'; +import { IAggType, IAggConfig, AggGroupNames, Schema } from '../legacy_imports'; -type AggId = AggConfig['id']; -type AggParams = AggConfig['params']; +type AggId = IAggConfig['id']; +type AggParams = IAggConfig['params']; export type AddSchema = (schemas: Schema) => void; -export type ReorderAggs = (sourceAgg: AggConfig, destinationAgg: AggConfig) => void; +export type ReorderAggs = (sourceAgg: IAggConfig, destinationAgg: IAggConfig) => void; export interface DefaultEditorCommonProps { formIsTouched: boolean; groupName: AggGroupNames; - metricAggs: AggConfig[]; + metricAggs: IAggConfig[]; state: VisState; setAggParamValue: ( aggId: AggId, paramName: T, value: AggParams[T] ) => void; - onAggTypeChange: (aggId: AggId, aggType: AggType) => void; + onAggTypeChange: (aggId: AggId, aggType: IAggType) => void; } export interface DefaultEditorAggCommonProps extends DefaultEditorCommonProps { diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.test.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.test.tsx index 9cbcc31bdc60e..c36c0176439f9 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.test.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.test.tsx @@ -21,7 +21,7 @@ import React from 'react'; import { mount, shallow } from 'enzyme'; import { act } from 'react-dom/test-utils'; import { VisState } from 'src/legacy/core_plugins/visualizations/public'; -import { AggConfigs, AggConfig, Schema } from '../legacy_imports'; +import { IAggConfigs, IAggConfig, Schema } from '../legacy_imports'; import { DefaultEditorAggGroup, DefaultEditorAggGroupProps } from './agg_group'; import { DefaultEditorAgg } from './agg'; import { DefaultEditorAggAdd } from './agg_add'; @@ -56,7 +56,7 @@ jest.mock('./agg_add', () => ({ describe('DefaultEditorAgg component', () => { let defaultProps: DefaultEditorAggGroupProps; - let aggs: AggConfigs; + let aggs: IAggConfigs; let setTouched: jest.Mock; let setValidity: jest.Mock; let reorderAggs: jest.Mock; @@ -76,7 +76,7 @@ describe('DefaultEditorAgg component', () => { }, }, schema: { group: 'metrics' }, - } as AggConfig, + } as IAggConfig, { id: '3', params: { @@ -85,7 +85,7 @@ describe('DefaultEditorAgg component', () => { }, }, schema: { group: 'metrics' }, - } as AggConfig, + } as IAggConfig, { id: '2', params: { @@ -94,9 +94,9 @@ describe('DefaultEditorAgg component', () => { }, }, schema: { group: 'buckets' }, - } as AggConfig, + } as IAggConfig, ], - } as AggConfigs; + } as IAggConfigs; defaultProps = { formIsTouched: false, diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.tsx index 3491414bec809..768a9669025e4 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.tsx @@ -30,7 +30,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { AggConfig, aggGroupNamesMap, AggGroupNames, Schema } from '../legacy_imports'; +import { IAggConfig, aggGroupNamesMap, AggGroupNames, Schema } from '../legacy_imports'; import { DefaultEditorAgg } from './agg'; import { DefaultEditorAggAdd } from './agg_add'; import { AddSchema, ReorderAggs, DefaultEditorAggCommonProps } from './agg_common_props'; @@ -69,8 +69,8 @@ function DefaultEditorAggGroup({ }: DefaultEditorAggGroupProps) { const groupNameLabel = (aggGroupNamesMap() as any)[groupName]; // e.g. buckets can have no aggs - const group: AggConfig[] = useMemo( - () => state.aggs.aggs.filter((agg: AggConfig) => agg.schema.group === groupName) || [], + const group: IAggConfig[] = useMemo( + () => state.aggs.aggs.filter((agg: IAggConfig) => agg.schema.group === groupName) || [], [groupName, state.aggs.aggs] ); @@ -151,7 +151,7 @@ function DefaultEditorAggGroup({ )} <> - {group.map((agg: AggConfig, index: number) => ( + {group.map((agg: IAggConfig, index: number) => ( { - let group: AggConfig[]; + let group: IAggConfig[]; beforeEach(() => { group = [ @@ -39,7 +39,7 @@ describe('DefaultEditorGroup helpers', () => { }, }, schema: { name: 'metric', min: 1, mustBeFirst: true }, - } as AggConfig, + } as IAggConfig, { id: '2', params: { @@ -48,7 +48,7 @@ describe('DefaultEditorGroup helpers', () => { }, }, schema: { name: 'metric', min: 2 }, - } as AggConfig, + } as IAggConfig, ]; }); diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group_helper.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/agg_group_helper.tsx index 87f0d00d50a1d..d2e8e5401c0f7 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group_helper.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_group_helper.tsx @@ -18,12 +18,12 @@ */ import { findIndex, isEmpty } from 'lodash'; -import { AggConfig } from '../legacy_imports'; +import { IAggConfig } from '../legacy_imports'; import { AggsState } from './agg_group_state'; -const isAggRemovable = (agg: AggConfig, group: AggConfig[]) => { +const isAggRemovable = (agg: IAggConfig, group: IAggConfig[]) => { const metricCount = group.reduce( - (count, aggregation: AggConfig) => + (count, aggregation: IAggConfig) => aggregation.schema.name === agg.schema.name ? ++count : count, 0 ); @@ -31,20 +31,20 @@ const isAggRemovable = (agg: AggConfig, group: AggConfig[]) => { return metricCount > agg.schema.min; }; -const getEnabledMetricAggsCount = (group: AggConfig[]) => { +const getEnabledMetricAggsCount = (group: IAggConfig[]) => { return group.reduce( - (count, aggregation: AggConfig) => + (count, aggregation: IAggConfig) => aggregation.schema.name === 'metric' && aggregation.enabled ? ++count : count, 0 ); }; -const calcAggIsTooLow = (agg: AggConfig, aggIndex: number, group: AggConfig[]) => { +const calcAggIsTooLow = (agg: IAggConfig, aggIndex: number, group: IAggConfig[]) => { if (!agg.schema.mustBeFirst) { return false; } - const firstDifferentSchema = findIndex(group, (aggr: AggConfig) => { + const firstDifferentSchema = findIndex(group, (aggr: IAggConfig) => { return aggr.schema !== agg.schema; }); diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group_state.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/agg_group_state.tsx index b06ca1c2ce57a..d022297ae72b3 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group_state.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_group_state.tsx @@ -17,7 +17,7 @@ * under the License. */ -import { AggConfig } from '../legacy_imports'; +import { IAggConfig } from '../legacy_imports'; export enum AGGS_ACTION_KEYS { TOUCHED = 'aggsTouched', @@ -52,7 +52,7 @@ function aggGroupReducer(state: AggsState, action: AggsAction): AggsState { } } -function initAggsState(group: AggConfig[]): AggsState { +function initAggsState(group: IAggConfig[]): AggsState { return group.reduce((state, agg) => { state[agg.id] = { touched: false, valid: true }; return state; 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..c858fb62045ca 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,15 +19,16 @@ import { Field } from 'src/plugins/data/public'; import { VisState } from 'src/legacy/core_plugins/visualizations/public'; -import { AggConfig, AggParam, EditorConfig } from '../legacy_imports'; +import { IAggConfig, 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 // https://github.com/babel/babel/issues/7641 // export interface AggParamCommonProps { - agg: AggConfig; + agg: IAggConfig; aggParam: P; disabled?: boolean; editorConfig: EditorConfig; @@ -36,7 +37,7 @@ export interface AggParamCommonProps { showValidation: boolean; state: VisState; value?: T; - metricAggs: AggConfig[]; + metricAggs: IAggConfig[]; } export interface AggParamEditorProps extends AggParamCommonProps { 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..af851aa9b4418 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 @@ -23,7 +23,7 @@ import { mount, shallow } from 'enzyme'; import { VisState } from 'src/legacy/core_plugins/visualizations/public'; import { IndexPattern } from 'src/plugins/data/public'; import { DefaultEditorAggParams, DefaultEditorAggParamsProps } from './agg_params'; -import { AggConfig, AggGroupNames } from '../legacy_imports'; +import { IAggConfig, AggGroupNames } from '../legacy_imports'; const mockEditorConfig = { useNormalizedEsInterval: { hidden: false, fixedValue: false }, @@ -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(() => ({ @@ -97,7 +95,7 @@ describe('DefaultEditorAggParams component', () => { schema: { title: '', }, - } as any) as AggConfig, + } as any) as IAggConfig, groupName: AggGroupNames.Metrics, formIsTouched: false, indexPattern: {} as IndexPattern, 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..e9583ab4cec79 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 { IAggConfig, 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'; @@ -54,7 +48,7 @@ type EditorParamConfigType = EditorParamConfig & { }; export interface DefaultEditorAggParamsProps extends DefaultEditorCommonProps { - agg: AggConfig; + agg: IAggConfig; aggError?: string; aggIndex?: number; aggIsTooLow?: boolean; @@ -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..f3bee80baa1ba 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 @@ -17,22 +17,16 @@ * under the License. */ -import { IndexPattern, Field } from 'src/plugins/data/public'; +import { IndexPattern } 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 { IAggConfig, IAggType, AggGroupNames, BUCKET_TYPES } 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']), @@ -42,10 +36,10 @@ jest.mock('ui/new_platform'); describe('DefaultEditorAggParams helpers', () => { describe('getAggParamsToRender', () => { - let agg: AggConfig; + let agg: IAggConfig; let editorConfig: EditorConfig; const state = {} as VisState; - const metricAggs: AggConfig[] = []; + const metricAggs: IAggConfig[] = []; const emptyParams = { basic: [], advanced: [], @@ -57,14 +51,14 @@ describe('DefaultEditorAggParams helpers', () => { params: [{ name: 'interval' }], }, schema: {}, - } as AggConfig; + } as IAggConfig; const params = getAggParamsToRender({ agg, editorConfig, metricAggs, state }); expect(params).toEqual(emptyParams); }); it('should not create any param if there is no agg type', () => { - agg = {} as AggConfig; + agg = {} as IAggConfig; const params = getAggParamsToRender({ agg, editorConfig, metricAggs, state }); expect(params).toEqual(emptyParams); @@ -75,7 +69,7 @@ describe('DefaultEditorAggParams helpers', () => { type: { params: [{ name: 'interval' }], }, - } as AggConfig; + } as IAggConfig; editorConfig = { interval: { hidden: true, @@ -94,7 +88,7 @@ describe('DefaultEditorAggParams helpers', () => { schema: { hideCustomLabel: true, }, - } as AggConfig; + } as IAggConfig; const params = getAggParamsToRender({ agg, editorConfig, metricAggs, state }); expect(params).toEqual(emptyParams); @@ -111,8 +105,10 @@ describe('DefaultEditorAggParams helpers', () => { name: 'field', type: 'field', filterFieldTypes, - getAvailableFields: jest.fn((fields: IndexedArray) => - fields.filter(({ type }) => filterFieldTypes.includes(type)) + getAvailableFields: jest.fn((aggConfig: IAggConfig) => + aggConfig + .getIndexPattern() + .fields.filter(({ type }) => filterFieldTypes.includes(type)) ), }, { @@ -131,7 +127,7 @@ describe('DefaultEditorAggParams helpers', () => { orderBy: 'orderBy', field: 'field', }, - } as any) as AggConfig; + } as any) as IAggConfig; const params = getAggParamsToRender({ agg, editorConfig, metricAggs, state }); expect(params).toEqual({ @@ -166,14 +162,14 @@ describe('DefaultEditorAggParams helpers', () => { describe('getAggTypeOptions', () => { it('should return agg type options grouped by subtype', () => { const indexPattern = {} as IndexPattern; - const aggs = getAggTypeOptions({} as AggConfig, indexPattern, 'metrics'); + const aggs = getAggTypeOptions({} as IAggConfig, indexPattern, 'metrics'); expect(aggs).toEqual(['indexedFields']); }); }); describe('isInvalidParamsTouched', () => { - let aggType: AggType; + let aggType: IAggType; const aggTypeState = { touched: false, valid: true, 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..124c41a50c0df 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 @@ -29,17 +29,17 @@ import { aggTypeFilters, aggTypeFieldFilters, aggTypes, - AggConfig, + IAggConfig, AggParam, - FieldParamType, - AggType, - EditorConfig, + IFieldParamType, + IAggType, } from '../legacy_imports'; +import { EditorConfig } from './utils'; interface ParamInstanceBase { - agg: AggConfig; + agg: IAggConfig; editorConfig: EditorConfig; - metricAggs: AggConfig[]; + metricAggs: IAggConfig[]; state: VisState; } @@ -73,9 +73,7 @@ function getAggParamsToRender({ agg, editorConfig, metricAggs, state }: ParamIns } // if field param exists, compute allowed fields if (param.type === 'field') { - const availableFields: Field[] = (param as FieldParamType).getAvailableFields( - agg.getIndexPattern().fields - ); + const availableFields: Field[] = (param as IFieldParamType).getAvailableFields(agg); fields = aggTypeFieldFilters.filter(availableFields, agg); indexedFields = groupAndSortBy(fields, 'type', 'name'); @@ -117,10 +115,10 @@ function getAggParamsToRender({ agg, editorConfig, metricAggs, state }: ParamIns } function getAggTypeOptions( - agg: AggConfig, + agg: IAggConfig, indexPattern: IndexPattern, groupName: string -): ComboBoxGroupedOptions { +): ComboBoxGroupedOptions { const aggTypeOptions = aggTypeFilters.filter((aggTypes as any)[groupName], indexPattern, agg); return groupAndSortBy(aggTypeOptions as any[], 'subtype', 'title'); } @@ -135,7 +133,7 @@ function getAggTypeOptions( * @param aggParams State of aggregation parameters. */ function isInvalidParamsTouched( - aggType: AggType, + aggType: IAggType, aggTypeState: AggTypeState, aggParams: AggParamsState ) { diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_select.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/agg_select.tsx index 2a9c74521e525..0ec19bfa1b843 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_select.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_select.tsx @@ -24,20 +24,20 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { IndexPattern } from 'src/plugins/data/public'; -import { AggType, documentationLinks } from '../legacy_imports'; +import { IAggType, documentationLinks } from '../legacy_imports'; import { ComboBoxGroupedOptions } from '../utils'; import { AGG_TYPE_ACTION_KEYS, AggTypeAction } from './agg_params_state'; interface DefaultEditorAggSelectProps { aggError?: string; - aggTypeOptions: ComboBoxGroupedOptions; + aggTypeOptions: ComboBoxGroupedOptions; id: string; indexPattern: IndexPattern; showValidation: boolean; isSubAggregation: boolean; - value: AggType; + value: IAggType; onChangeAggType: React.Dispatch; - setValue: (aggType: AggType) => void; + setValue: (aggType: IAggType) => void; } function DefaultEditorAggSelect({ @@ -51,7 +51,7 @@ function DefaultEditorAggSelect({ isSubAggregation, onChangeAggType, }: DefaultEditorAggSelectProps) { - const selectedOptions: ComboBoxGroupedOptions = value + const selectedOptions: ComboBoxGroupedOptions = value ? [{ label: value.title, target: value }] : []; @@ -104,7 +104,7 @@ function DefaultEditorAggSelect({ (options: EuiComboBoxOptionProps[]) => { const selectedOption = get(options, '0.target'); if (selectedOption) { - setValue(selectedOption as AggType); + setValue(selectedOption as IAggType); } }, [setValue] diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/agg_control_props.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/controls/agg_control_props.tsx index c8b5196d3b299..7f04b851902de 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/agg_control_props.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/agg_control_props.tsx @@ -18,11 +18,11 @@ */ import { VisParams } from 'src/legacy/core_plugins/visualizations/public'; -import { AggConfig } from '../../legacy_imports'; +import { IAggConfig } from '../../legacy_imports'; import { DefaultEditorAggCommonProps } from '../agg_common_props'; export interface AggControlProps { - agg: AggConfig; + agg: IAggConfig; editorStateParams: VisParams; setAggParamValue: DefaultEditorAggCommonProps['setAggParamValue']; setStateParamValue: DefaultEditorAggCommonProps['setStateParamValue']; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/agg_utils.test.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/controls/agg_utils.test.tsx index 5c69fd0f1c091..0b847e3747b30 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/agg_utils.test.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/agg_utils.test.tsx @@ -20,7 +20,7 @@ import React, { FunctionComponent } from 'react'; import { mount, ReactWrapper } from 'enzyme'; -import { AggConfig } from '../../legacy_imports'; +import { IAggConfig } from '../../legacy_imports'; import { safeMakeLabel, useAvailableOptions, @@ -57,7 +57,7 @@ const metricAggs = [ return 'avg'; }, }, -] as AggConfig[]; +] as IAggConfig[]; const incompatibleAggs = [ { @@ -74,7 +74,7 @@ const incompatibleAggs = [ return 'percentiles'; }, }, -] as AggConfig[]; +] as IAggConfig[]; const aggFilter = ['!top_hits', '!percentiles']; describe('Aggregations utils', () => { @@ -222,7 +222,7 @@ describe('Aggregations utils', () => { }); test('should not fail and return a safety string if makeLabel func is not exist', () => { - const label = safeMakeLabel({} as AggConfig); + const label = safeMakeLabel({} as IAggConfig); expect(label).toEqual(expect.any(String)); }); diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/field.test.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/controls/field.test.tsx index e43304fe07347..636ef8f872d0e 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/field.test.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/field.test.tsx @@ -26,7 +26,7 @@ import { Field } from 'src/plugins/data/public'; import { VisState } from 'src/legacy/core_plugins/visualizations/public'; import { ComboBoxGroupedOptions } from '../../utils'; import { FieldParamEditor, FieldParamEditorProps } from './field'; -import { AggConfig } from '../../legacy_imports'; +import { IAggConfig } from '../../legacy_imports'; function callComboBoxOnChange(comp: ReactWrapper, value: any = []) { const comboBoxProps: EuiComboBoxProps = comp.find(EuiComboBox).props(); @@ -64,7 +64,7 @@ describe('FieldParamEditor component', () => { ]; defaultProps = { - agg: {} as AggConfig, + agg: {} as IAggConfig, aggParam: { name: 'field', type: 'field', @@ -80,7 +80,7 @@ describe('FieldParamEditor component', () => { setValidity, setTouched, state: {} as VisState, - metricAggs: [] as AggConfig[], + metricAggs: [] as IAggConfig[], }; }); diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/field.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/controls/field.tsx index 38c55e8fe3f24..f374353afabec 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/field.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/field.tsx @@ -24,7 +24,7 @@ import { EuiComboBox, EuiComboBoxOptionProps, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Field } from 'src/plugins/data/public'; -import { AggConfig, AggParam, FieldParamType } from '../../legacy_imports'; +import { AggParam, IAggConfig, IFieldParamType } from '../../legacy_imports'; import { formatListAsProse, parseCommaSeparatedList, useValidation } from './utils'; import { AggParamEditorProps } from '../agg_param_props'; import { ComboBoxGroupedOptions } from '../../utils'; @@ -126,9 +126,10 @@ function FieldParamEditor({ ); } -function getFieldTypesString(agg: AggConfig) { +function getFieldTypesString(agg: IAggConfig) { const param = - get(agg, 'type.params', []).find((p: AggParam) => p.name === 'field') || ({} as FieldParamType); + get(agg, 'type.params', []).find((p: AggParam) => p.name === 'field') || + ({} as IFieldParamType); return formatListAsProse(parseCommaSeparatedList(param.filterFieldTypes), { inclusive: false }); } diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/filter.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/controls/filter.tsx index 38c5b552553ae..3622b27bad403 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/filter.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/filter.tsx @@ -22,7 +22,7 @@ import { EuiForm, EuiButtonIcon, EuiFieldText, EuiFormRow, EuiSpacer } from '@el import { i18n } from '@kbn/i18n'; import { Query, QueryStringInput } from '../../../../../../plugins/data/public'; -import { AggConfig } from '../../legacy_imports'; +import { IAggConfig } from '../../legacy_imports'; interface FilterRowProps { id: string; @@ -34,7 +34,7 @@ interface FilterRowProps { dataTestSubj: string; onChangeValue(id: string, query: Query, label: string): void; onRemoveFilter(id: string): void; - agg: AggConfig; + agg: IAggConfig; } function FilterRow({ diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/metric_agg.test.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/controls/metric_agg.test.tsx index 9b6fd204e7207..cf7af1aa5cb3a 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/metric_agg.test.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/metric_agg.test.tsx @@ -20,7 +20,7 @@ import React from 'react'; import { mount, shallow } from 'enzyme'; -import { AggConfig } from '../../legacy_imports'; +import { IAggConfig } from '../../legacy_imports'; import { DEFAULT_OPTIONS, aggFilter, MetricAggParamEditor } from './metric_agg'; jest.mock('./utils', () => ({ @@ -44,7 +44,7 @@ const agg = { makeLabel() { return 'cumulative_sum'; }, -} as AggConfig; +} as IAggConfig; const metricAggs = [ agg, @@ -69,7 +69,7 @@ const metricAggs = [ return 'max'; }, }, -] as AggConfig[]; +] as IAggConfig[]; describe('MetricAggParamEditor', () => { let defaultProps: Partial>; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/order_agg.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/controls/order_agg.tsx index 6bb9ad334d149..10679b578d54e 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/order_agg.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/order_agg.tsx @@ -20,7 +20,7 @@ import React, { useEffect } from 'react'; import { EuiSpacer } from '@elastic/eui'; -import { AggParamType, AggConfig, AggGroupNames } from '../../legacy_imports'; +import { AggParamType, IAggConfig, AggGroupNames } from '../../legacy_imports'; import { useSubAggParamsHandlers } from './utils'; import { AggParamEditorProps } from '../agg_param_props'; import { DefaultEditorAggParams } from '../agg_params'; @@ -35,7 +35,7 @@ function OrderAggParamEditor({ setValue, setValidity, setTouched, -}: AggParamEditorProps) { +}: AggParamEditorProps) { const orderBy = agg.params.orderBy; useEffect(() => { @@ -51,7 +51,7 @@ function OrderAggParamEditor({ const { onAggTypeChange, setAggParamValue } = useSubAggParamsHandlers( agg, aggParam, - value as AggConfig, + value as IAggConfig, setValue ); @@ -63,7 +63,7 @@ function OrderAggParamEditor({ <> ) { +}: AggParamEditorProps) { useEffect(() => { // we aren't creating a custom aggConfig if (agg.params.metricAgg !== 'custom') { diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/sub_metric.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/controls/sub_metric.tsx index 9898d943870bc..45ff0610d88ed 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/sub_metric.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/sub_metric.tsx @@ -21,7 +21,7 @@ import React, { useEffect } from 'react'; import { EuiFormLabel, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { AggParamType, AggConfig, AggGroupNames } from '../../legacy_imports'; +import { AggParamType, IAggConfig, AggGroupNames } from '../../legacy_imports'; import { useSubAggParamsHandlers } from './utils'; import { AggParamEditorProps } from '../agg_param_props'; import { DefaultEditorAggParams } from '../agg_params'; @@ -35,7 +35,7 @@ function SubMetricParamEditor({ setValue, setValidity, setTouched, -}: AggParamEditorProps) { +}: AggParamEditorProps) { const metricTitle = i18n.translate('visDefaultEditor.controls.metrics.metricTitle', { defaultMessage: 'Metric', }); 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..4280f85c901d7 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,14 +18,15 @@ */ import { VisState } from 'src/legacy/core_plugins/visualizations/public'; -import { AggConfig, AggParam, EditorConfig } from '../../legacy_imports'; +import { IAggConfig, AggParam } from '../../legacy_imports'; +import { EditorConfig } from '../utils'; export const aggParamCommonPropsMock = { - agg: {} as AggConfig, + agg: {} as IAggConfig, aggParam: {} as AggParam, editorConfig: {} as EditorConfig, formIsTouched: false, - metricAggs: [] as AggConfig[], + metricAggs: [] as IAggConfig[], state: {} as VisState, showValidation: false, }; 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/controls/top_aggregate.test.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/controls/top_aggregate.test.tsx index b0c3fe00606aa..4ce0712040bd5 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/top_aggregate.test.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/top_aggregate.test.tsx @@ -25,10 +25,10 @@ import { TopAggregateParamEditorProps, } from './top_aggregate'; import { aggParamCommonPropsMock } from './test_utils'; -import { AggConfig } from '../../legacy_imports'; +import { IAggConfig } from '../../legacy_imports'; describe('TopAggregateParamEditor', () => { - let agg: AggConfig; + let agg: IAggConfig; let aggParam: any; let defaultProps: TopAggregateParamEditorProps; let options: AggregateValueProp[]; @@ -37,17 +37,17 @@ describe('TopAggregateParamEditor', () => { options = [ { text: 'Min', - isCompatible: jest.fn((aggr: AggConfig) => aggr.params.field.type === 'number'), + isCompatible: jest.fn((aggr: IAggConfig) => aggr.params.field.type === 'number'), value: 'min', }, { text: 'Max', - isCompatible: jest.fn((aggr: AggConfig) => aggr.params.field.type === 'number'), + isCompatible: jest.fn((aggr: IAggConfig) => aggr.params.field.type === 'number'), value: 'max', }, { text: 'Average', - isCompatible: jest.fn((aggr: AggConfig) => aggr.params.field.type === 'string'), + isCompatible: jest.fn((aggr: IAggConfig) => aggr.params.field.type === 'string'), value: 'average', }, ]; @@ -69,7 +69,7 @@ describe('TopAggregateParamEditor', () => { }, }, getAggParams: jest.fn(() => [{ name: 'aggregate', options }]), - } as any) as AggConfig; + } as any) as IAggConfig; defaultProps = { ...aggParamCommonPropsMock, agg, @@ -150,7 +150,7 @@ describe('TopAggregateParamEditor', () => { type: 'string', }, }, - } as AggConfig; + } as IAggConfig; comp.setProps({ agg }); @@ -165,7 +165,7 @@ describe('TopAggregateParamEditor', () => { type: 'date', }, }, - } as AggConfig; + } as IAggConfig; comp.setProps({ agg }); @@ -179,7 +179,7 @@ describe('TopAggregateParamEditor', () => { type: 'string', }, }, - } as AggConfig; + } as IAggConfig; comp.setProps({ agg, value: undefined }); diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/top_aggregate.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/controls/top_aggregate.tsx index 338e2fe463a80..346dfc0156f07 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/top_aggregate.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/top_aggregate.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { - AggConfig, + IAggConfig, AggParam, OptionedValueProp, OptionedParamEditorProps, @@ -32,13 +32,13 @@ import { import { AggParamEditorProps } from '../agg_param_props'; export interface AggregateValueProp extends OptionedValueProp { - isCompatible(aggConfig: AggConfig): boolean; + isCompatible(aggConfig: IAggConfig): boolean; } export type TopAggregateParamEditorProps = AggParamEditorProps & OptionedParamEditorProps; -export function getCompatibleAggs(agg: AggConfig): AggregateValueProp[] { +export function getCompatibleAggs(agg: IAggConfig): AggregateValueProp[] { const { options = [] } = agg .getAggParams() .find(({ name }: AggParam) => name === 'aggregate') as OptionedParamType; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/agg_utils.ts b/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/agg_utils.ts index 4c8ba23e63268..8aeae488942cd 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/agg_utils.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/agg_utils.ts @@ -20,7 +20,7 @@ import { useEffect, useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; -import { AggConfig } from '../../../legacy_imports'; +import { IAggConfig } from '../../../legacy_imports'; type AggFilter = string[]; @@ -43,7 +43,7 @@ function useCompatibleAggCallback(aggFilter: AggFilter) { function useFallbackMetric( setValue: (value?: string) => void, aggFilter: AggFilter, - metricAggs?: AggConfig[], + metricAggs?: IAggConfig[], value?: string, fallbackValue?: string ) { @@ -69,7 +69,7 @@ function useFallbackMetric( */ function useAvailableOptions( aggFilter: AggFilter, - metricAggs: AggConfig[] = [], + metricAggs: IAggConfig[] = [], defaultOptions: Array<{ text: string; value: string }> = [] ) { const isCompatibleAgg = useCompatibleAggCallback(aggFilter); @@ -107,7 +107,7 @@ function useValidation(setValidity: (isValid: boolean) => void, isValid: boolean }, [isValid, setValidity]); } -function safeMakeLabel(agg: AggConfig): string { +function safeMakeLabel(agg: IAggConfig): string { try { return agg.makeLabel(); } catch (e) { @@ -118,7 +118,7 @@ function safeMakeLabel(agg: AggConfig): string { } function isCompatibleAggregation(aggFilter: string[]) { - return (agg: AggConfig) => { + return (agg: IAggConfig) => { return !aggFilter.includes(`!${agg.type.name}`); }; } diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/use_handlers.ts b/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/use_handlers.ts index c2da648edcf81..c7816d5a9d305 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/use_handlers.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/use_handlers.ts @@ -19,14 +19,14 @@ import { useCallback } from 'react'; -import { AggConfig, AggParamType } from '../../../legacy_imports'; +import { IAggConfig, AggParamType } from '../../../legacy_imports'; -type SetValue = (value?: AggConfig) => void; +type SetValue = (value?: IAggConfig) => void; function useSubAggParamsHandlers( - agg: AggConfig, + agg: IAggConfig, aggParam: AggParamType, - subAgg: AggConfig, + subAgg: IAggConfig, setValue: SetValue ) { const setAggParamValue = useCallback( diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/data_tab.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/data_tab.tsx index c7597ef43dfa6..efd17f02a0e09 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/data_tab.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/data_tab.tsx @@ -23,11 +23,11 @@ import { EuiSpacer } from '@elastic/eui'; import { VisState } from 'src/legacy/core_plugins/visualizations/public'; import { - AggConfig, + IAggConfig, AggGroupNames, ISchemas, parentPipelineType, - MetricAggType, + IMetricAggType, } from '../../legacy_imports'; import { DefaultEditorAggGroup } from '../agg_group'; import { @@ -45,7 +45,7 @@ export interface DefaultEditorDataTabProps { dispatch: React.Dispatch; formIsTouched: boolean; isTabSelected: boolean; - metricAggs: AggConfig[]; + metricAggs: IAggConfig[]; schemas: ISchemas; state: VisState; setTouched(isTouched: boolean): void; @@ -67,7 +67,7 @@ function DefaultEditorDataTab({ () => findLast( metricAggs, - ({ type }: { type: MetricAggType }) => type.subtype === parentPipelineType + ({ type }: { type: IMetricAggType }) => type.subtype === parentPipelineType ), [metricAggs] ); diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/actions.ts b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/actions.ts index 5738916d2ff80..93fa1083bebf9 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/actions.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/actions.ts @@ -18,7 +18,7 @@ */ import { Vis, VisParams } from 'src/legacy/core_plugins/visualizations/public'; -import { AggConfig, Schema } from '../../../legacy_imports'; +import { IAggConfig, Schema } from '../../../legacy_imports'; import { EditorStateActionTypes } from './constants'; export interface ActionType { @@ -26,14 +26,14 @@ export interface ActionType { payload: P; } -type AggId = AggConfig['id']; -type AggParams = AggConfig['params']; +type AggId = IAggConfig['id']; +type AggParams = IAggConfig['params']; type AddNewAgg = ActionType; type DiscardChanges = ActionType; type ChangeAggType = ActionType< EditorStateActionTypes.CHANGE_AGG_TYPE, - { aggId: AggId; value: AggConfig['type'] } + { aggId: AggId; value: IAggConfig['type'] } >; type SetAggParamValue = ActionType< EditorStateActionTypes.SET_AGG_PARAM_VALUE, @@ -50,11 +50,11 @@ type SetStateParamValue = ActionTyp type RemoveAgg = ActionType; type ReorderAggs = ActionType< EditorStateActionTypes.REORDER_AGGS, - { sourceAgg: AggConfig; destinationAgg: AggConfig } + { sourceAgg: IAggConfig; destinationAgg: IAggConfig } >; type ToggleEnabledAgg = ActionType< EditorStateActionTypes.TOGGLE_ENABLED_AGG, - { aggId: AggId; enabled: AggConfig['enabled'] } + { aggId: AggId; enabled: IAggConfig['enabled'] } >; type UpdateStateParams = ActionType< EditorStateActionTypes.UPDATE_STATE_PARAMS, @@ -75,7 +75,7 @@ export type EditorAction = export interface EditorActions { addNewAgg(schema: Schema): AddNewAgg; discardChanges(vis: Vis): DiscardChanges; - changeAggType(aggId: AggId, value: AggConfig['type']): ChangeAggType; + changeAggType(aggId: AggId, value: IAggConfig['type']): ChangeAggType; setAggParamValue( aggId: AggId, paramName: T, @@ -86,8 +86,8 @@ export interface EditorActions { value: AggParams[T] ): SetStateParamValue; removeAgg(aggId: AggId): RemoveAgg; - reorderAggs(sourceAgg: AggConfig, destinationAgg: AggConfig): ReorderAggs; - toggleEnabledAgg(aggId: AggId, enabled: AggConfig['enabled']): ToggleEnabledAgg; + reorderAggs(sourceAgg: IAggConfig, destinationAgg: IAggConfig): ReorderAggs; + toggleEnabledAgg(aggId: AggId, enabled: IAggConfig['enabled']): ToggleEnabledAgg; updateStateParams(params: VisParams): UpdateStateParams; } diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/reducers.ts b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/reducers.ts index 8e1cfd6bc9c13..851263f0ed702 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/reducers.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/reducers.ts @@ -20,7 +20,7 @@ import { cloneDeep } from 'lodash'; import { Vis, VisState } from 'src/legacy/core_plugins/visualizations/public'; -import { AggConfigs, AggConfig, AggGroupNames, move } from '../../../legacy_imports'; +import { AggConfigs, IAggConfig, AggGroupNames, move } from '../../../legacy_imports'; import { EditorStateActionTypes } from './constants'; import { getEnabledMetricAggsCount } from '../../agg_group_helper'; import { EditorAction } from './actions'; @@ -32,7 +32,7 @@ function initEditorState(vis: Vis) { function editorStateReducer(state: VisState, action: EditorAction): VisState { switch (action.type) { case EditorStateActionTypes.ADD_NEW_AGG: { - const aggConfig = state.aggs.createAggConfig(action.payload as AggConfig, { + const aggConfig = state.aggs.createAggConfig(action.payload as IAggConfig, { addToAggConfigs: false, }); aggConfig.brandNew = true; 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..80a64b7289f8c --- /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 configuration interval: {interval}', + values: { interval }, + }), + }, + } + : {}; + } + + // Set date histogram time zone based on agg restrictions + 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..b7fd6b1e9ebb6 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 @@ -20,39 +20,36 @@ /* `ui/agg_types` dependencies */ export { AggType, - AggConfig, + IAggType, + IAggConfig, AggConfigs, + IAggConfigs, AggParam, AggGroupNames, aggGroupNamesMap, aggTypes, FieldParamType, + IFieldParamType, BUCKET_TYPES, METRIC_TYPES, ISchemas, Schema, termsAggFilter, } from 'ui/agg_types'; -export { aggTypeFilters, propFilter } from 'ui/agg_types/filter'; -export { aggTypeFieldFilters } from 'ui/agg_types/param_types/filter'; -export { AggParamType } from 'ui/agg_types/param_types/agg'; -export { MetricAggType } from 'ui/agg_types/metrics/metric_agg_type'; -export { parentPipelineType } from 'ui/agg_types/metrics/lib/parent_pipeline_agg_helper'; -export { siblingPipelineType } from 'ui/agg_types/metrics/lib/sibling_pipeline_agg_helper'; -export { isType, isStringType } from 'ui/agg_types/buckets/migrate_include_exclude_format'; -export { - OptionedValueProp, - OptionedParamEditorProps, - OptionedParamType, -} from 'ui/agg_types/param_types/optioned'; -export { isValidJson, isValidInterval } from 'ui/agg_types/utils'; -export { AggParamOption } from 'ui/agg_types/agg_params'; -export { CidrMask } from 'ui/agg_types/buckets/lib/cidr_mask'; +export { aggTypeFilters, propFilter } from 'ui/agg_types'; +export { aggTypeFieldFilters } from 'ui/agg_types'; +export { AggParamType } from 'ui/agg_types'; +export { MetricAggType, IMetricAggType } from 'ui/agg_types'; +export { parentPipelineType } from 'ui/agg_types'; +export { siblingPipelineType } from 'ui/agg_types'; +export { isType, isStringType } from 'ui/agg_types'; +export { OptionedValueProp, OptionedParamEditorProps, OptionedParamType } from 'ui/agg_types'; +export { isValidJson, isValidInterval } from 'ui/agg_types'; +export { AggParamOption } from 'ui/agg_types'; +export { CidrMask } from 'ui/agg_types'; export { PersistedState } from 'ui/persisted_state'; -export { IndexedArray } from 'ui/indexed_array'; 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/core_plugins/vis_default_editor/public/vis_options_props.tsx b/src/legacy/core_plugins/vis_default_editor/public/vis_options_props.tsx index f51e359d99573..babcb59c6582e 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/vis_options_props.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/vis_options_props.tsx @@ -17,11 +17,11 @@ * under the License. */ -import { AggConfigs, PersistedState } from './legacy_imports'; +import { IAggConfigs, PersistedState } from './legacy_imports'; import { Vis } from '../../visualizations/public'; export interface VisOptionsProps { - aggs: AggConfigs; + aggs: IAggConfigs; hasHistogramAgg: boolean; isTabSelected: boolean; stateParams: VisParamType; diff --git a/src/legacy/core_plugins/vis_default_editor/public/vis_type_agg_filter.ts b/src/legacy/core_plugins/vis_default_editor/public/vis_type_agg_filter.ts index c1832d5512817..60b675f50a342 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/vis_type_agg_filter.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/vis_type_agg_filter.ts @@ -17,7 +17,7 @@ * under the License. */ import { IndexPattern } from 'src/plugins/data/public'; -import { AggType, AggConfig, aggTypeFilters, propFilter } from './legacy_imports'; +import { IAggType, IAggConfig, aggTypeFilters, propFilter } from './legacy_imports'; const filterByName = propFilter('name'); @@ -25,7 +25,9 @@ const filterByName = propFilter('name'); * This filter checks the defined aggFilter in the schemas of that visualization * and limits available aggregations based on that. */ -aggTypeFilters.addFilter((aggType: AggType, indexPatterns: IndexPattern, aggConfig: AggConfig) => { - const doesSchemaAllowAggType = filterByName([aggType], aggConfig.schema.aggFilter).length !== 0; - return doesSchemaAllowAggType; -}); +aggTypeFilters.addFilter( + (aggType: IAggType, indexPatterns: IndexPattern, aggConfig: IAggConfig) => { + const doesSchemaAllowAggType = filterByName([aggType], aggConfig.schema.aggFilter).length !== 0; + return doesSchemaAllowAggType; + } +); diff --git a/src/legacy/core_plugins/vis_type_table/public/legacy_imports.ts b/src/legacy/core_plugins/vis_type_table/public/legacy_imports.ts index 8a454957b7ab9..efa6c0029e6d1 100644 --- a/src/legacy/core_plugins/vis_type_table/public/legacy_imports.ts +++ b/src/legacy/core_plugins/vis_type_table/public/legacy_imports.ts @@ -19,7 +19,7 @@ export { npSetup, npStart } from 'ui/new_platform'; export { getFormat } from 'ui/visualize/loader/pipeline_helpers/utilities'; -export { AggConfig, AggGroupNames, Schemas } from 'ui/agg_types'; +export { IAggConfig, AggGroupNames, Schemas } from 'ui/agg_types'; // @ts-ignore export { PrivateProvider } from 'ui/private/private'; diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts b/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts index 16181a3f70ff1..d8912975227bf 100644 --- a/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts +++ b/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts @@ -27,7 +27,7 @@ import './table_vis.mock'; import StubIndexPattern from 'test_utils/stub_index_pattern'; import { getAngularModule } from './get_inner_angular'; import { initTableVisLegacyModule } from './table_vis_legacy_module'; -import { npStart, AggConfig, tabifyAggResponse } from './legacy_imports'; +import { npStart, IAggConfig, tabifyAggResponse } from './legacy_imports'; import { tableVisTypeDefinition } from './table_vis_type'; import { Vis } from '../../visualizations/public'; import { setup as visualizationsSetup } from '../../visualizations/public/np_ready/public/legacy'; @@ -148,7 +148,7 @@ describe('Table Vis - Controller', () => { // basically a parameterized beforeEach function initController(vis: Vis) { - vis.aggs.aggs.forEach((agg: AggConfig, i: number) => { + vis.aggs.aggs.forEach((agg: IAggConfig, i: number) => { agg.id = 'agg_' + (i + 1); }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.test.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.test.tsx index 514b957765a99..944ed7e20d1f7 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.test.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.test.tsx @@ -27,7 +27,7 @@ import { Positions } from '../../../utils/collections'; import { ValueAxesPanel } from './value_axes_panel'; import { CategoryAxisPanel } from './category_axis_panel'; import { ChartTypes } from '../../../utils/collections'; -import { AggConfig, AggType } from '../../../legacy_imports'; +import { IAggConfig, IAggType } from '../../../legacy_imports'; import { defaultValueAxisId, valueAxis, seriesParam, categoryAxis } from './mocks'; jest.mock('ui/new_platform'); @@ -44,17 +44,17 @@ jest.mock('./value_axes_panel', () => ({ const SERIES_PARAMS = 'seriesParams'; const VALUE_AXES = 'valueAxes'; -const aggCount: AggConfig = { +const aggCount: IAggConfig = { id: '1', type: { name: 'count' }, makeLabel: () => 'Count', -} as AggConfig; +} as IAggConfig; -const aggAverage: AggConfig = { +const aggAverage: IAggConfig = { id: '2', - type: { name: 'average' } as AggType, + type: { name: 'average' } as IAggType, makeLabel: () => 'Average', -} as AggConfig; +} as IAggConfig; const createAggs = (aggs: any[]) => ({ aggs, diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx index c4dcbfaa47265..cdc8996f3fdeb 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx @@ -21,7 +21,7 @@ import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { cloneDeep, uniq, get } from 'lodash'; import { EuiSpacer } from '@elastic/eui'; -import { AggConfig } from '../../../legacy_imports'; +import { IAggConfig } from '../../../legacy_imports'; import { BasicVislibParams, ValueAxis, SeriesParam, Axis } from '../../../types'; import { ValidationVisOptionsProps } from '../../common'; import { SeriesPanel } from './series_panel'; @@ -99,7 +99,7 @@ function MetricsAxisOptions(props: ValidationVisOptionsProps) stateParams.valueAxes.forEach((axis, axisNumber) => { let newCustomLabel = ''; - const matchingSeries: AggConfig[] = []; + const matchingSeries: IAggConfig[] = []; series.forEach((serie, seriesIndex) => { if ((axisNumber === 0 && !serie.valueAxis) || serie.valueAxis === axis.id) { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts b/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts index df7278f2b761f..50a91df01de7c 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts @@ -17,7 +17,7 @@ * under the License. */ -export { AggType, AggConfig, AggGroupNames, Schemas } from 'ui/agg_types'; +export { AggType, AggGroupNames, IAggConfig, IAggType, Schemas } from 'ui/agg_types'; // @ts-ignore export { SimpleEmitter } from 'ui/utils/simple_emitter'; // @ts-ignore diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/query_geohash_bounds.ts b/src/legacy/core_plugins/visualizations/public/embeddable/query_geohash_bounds.ts index 46ade8ce465c0..719d69e21a826 100644 --- a/src/legacy/core_plugins/visualizations/public/embeddable/query_geohash_bounds.ts +++ b/src/legacy/core_plugins/visualizations/public/embeddable/query_geohash_bounds.ts @@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; import { toastNotifications } from 'ui/notify'; -import { AggConfig } from 'ui/agg_types'; +import { IAggConfig } from 'ui/agg_types'; import { timefilter } from 'ui/timefilter'; import { Vis } from '../np_ready/public'; import { esFilters, Query, SearchSource, ISearchSource } from '../../../../../plugins/data/public'; @@ -42,7 +42,7 @@ interface QueryGeohashBoundsParams { * TODO: Remove this as a part of elastic/kibana#30593 */ export async function queryGeohashBounds(vis: Vis, params: QueryGeohashBoundsParams) { - const agg = vis.getAggConfig().aggs.find((a: AggConfig) => { + const agg = vis.getAggConfig().aggs.find((a: IAggConfig) => { return get(a, 'type.dslName') === 'geohash_grid'; }); diff --git a/src/legacy/core_plugins/visualizations/public/legacy_imports.ts b/src/legacy/core_plugins/visualizations/public/legacy_imports.ts index 3088c4e67a3b7..5cff588d951b0 100644 --- a/src/legacy/core_plugins/visualizations/public/legacy_imports.ts +++ b/src/legacy/core_plugins/visualizations/public/legacy_imports.ts @@ -18,12 +18,13 @@ */ export { PersistedState } from '../../../ui/public/persisted_state'; -export { AggConfig } from '../../../ui/public/agg_types/agg_config'; -export { AggConfigs } from '../../../ui/public/agg_types/agg_configs'; export { + AggConfigs, + IAggConfig, + IAggConfigs, isDateHistogramBucketAggConfig, setBounds, -} from '../../../ui/public/agg_types/buckets/date_histogram'; +} from '../../../ui/public/agg_types'; export { createFormat } from '../../../ui/public/visualize/loader/pipeline_helpers/utilities'; export { I18nContext } from '../../../ui/public/i18n'; import chrome from '../../../ui/public/chrome'; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts index e733bad2c0127..d1017de35474a 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts @@ -27,11 +27,11 @@ import { Schemas, } from './build_pipeline'; import { Vis, VisState } from '..'; -import { AggConfig } from '../../../legacy_imports'; +import { IAggConfig } from '../../../legacy_imports'; import { searchSourceMock } from '../../../legacy_mocks'; jest.mock('ui/new_platform'); -jest.mock('ui/agg_types/buckets/date_histogram', () => ({ +jest.mock('ui/agg_types', () => ({ setBounds: () => {}, dateHistogramBucketAgg: () => {}, isDateHistogramBucketAggConfig: () => true, @@ -367,7 +367,7 @@ describe('visualize loader pipeline helpers: build pipeline', () => { }); describe('buildVislibDimensions', () => { - let aggs: AggConfig[]; + let aggs: IAggConfig[]; let visState: any; let vis: Vis; let params: any; @@ -385,7 +385,7 @@ describe('visualize loader pipeline helpers: build pipeline', () => { name: 'metric', }, params: {}, - } as AggConfig, + } as IAggConfig, ]; params = { @@ -453,7 +453,7 @@ describe('visualize loader pipeline helpers: build pipeline', () => { it('with two numeric metrics, mixed normal and percent mode should have corresponding formatters', async () => { const aggConfig = aggs[0]; - aggs = [{ ...aggConfig } as AggConfig, { ...aggConfig, id: '5' } as AggConfig]; + aggs = [{ ...aggConfig } as IAggConfig, { ...aggConfig, id: '5' } as IAggConfig]; visState = { params: { diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts index 6749a44b4d5b3..04a296a888e87 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts @@ -23,7 +23,7 @@ import moment from 'moment'; import { SerializedFieldFormat } from 'src/plugins/expressions/public'; import { ISearchSource } from 'src/plugins/data/public'; import { - AggConfig, + IAggConfig, setBounds, isDateHistogramBucketAggConfig, createFormat, @@ -85,7 +85,7 @@ const vislibCharts: string[] = [ ]; export const getSchemas = (vis: Vis, timeRange?: any): Schemas => { - const createSchemaConfig = (accessor: number, agg: AggConfig): SchemaConfig => { + const createSchemaConfig = (accessor: number, agg: IAggConfig): SchemaConfig => { if (isDateHistogramBucketAggConfig(agg)) { agg.params.timeRange = timeRange; setBounds(agg, true); @@ -130,10 +130,10 @@ export const getSchemas = (vis: Vis, timeRange?: any): Schemas => { const schemas: Schemas = { metric: [], }; - const responseAggs = vis.aggs.getResponseAggs().filter((agg: AggConfig) => agg.enabled); + const responseAggs = vis.aggs.getResponseAggs().filter((agg: IAggConfig) => agg.enabled); const isHierarchical = vis.isHierarchical(); - const metrics = responseAggs.filter((agg: AggConfig) => agg.type.type === 'metrics'); - responseAggs.forEach((agg: AggConfig) => { + const metrics = responseAggs.filter((agg: IAggConfig) => agg.type.type === 'metrics'); + responseAggs.forEach((agg: IAggConfig) => { let skipMetrics = false; let schemaName = agg.schema ? agg.schema.name || agg.schema : null; if (typeof schemaName === 'object') { @@ -224,7 +224,7 @@ export const prepareDimension = (variable: string, data: any) => { const adjustVislibDimensionFormmaters = (vis: Vis, dimensions: { y: any[] }): void => { const visState = vis.getCurrentState(); const visConfig = visState.params; - const responseAggs = vis.aggs.getResponseAggs().filter((agg: AggConfig) => agg.enabled); + const responseAggs = vis.aggs.getResponseAggs().filter((agg: IAggConfig) => agg.enabled); (dimensions.y || []).forEach(yDimension => { const yAgg = responseAggs[yDimension.accessor]; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.d.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.d.ts index 6e6a2174d6ad1..71bf9bcf983ff 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.d.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.d.ts @@ -18,7 +18,7 @@ */ import { VisType } from './types'; -import { AggConfigs } from '../../legacy_imports'; +import { IAggConfigs } from '../../legacy_imports'; import { Status } from './legacy/update_status'; export interface Vis { @@ -39,7 +39,7 @@ export interface VisState { title: string; type: VisType; params: VisParams; - aggs: AggConfigs; + aggs: IAggConfigs; } export declare class VisualizationController { diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.js index 4f1526c20cb6f..0c2e5012df439 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.js +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.js @@ -21,7 +21,7 @@ * @name Vis * * @description This class consists of aggs, params, listeners, title, and type. - * - Aggs: Instances of AggConfig. + * - Aggs: Instances of IAggConfig. * - Params: The settings in the Options tab. * * Not to be confused with vislib/vis.js. diff --git a/src/legacy/ui/public/agg_response/tabify/_get_columns.ts b/src/legacy/ui/public/agg_response/tabify/_get_columns.ts index a3127c039049b..4144d5be16012 100644 --- a/src/legacy/ui/public/agg_response/tabify/_get_columns.ts +++ b/src/legacy/ui/public/agg_response/tabify/_get_columns.ts @@ -18,15 +18,15 @@ */ import { groupBy } from 'lodash'; -import { AggConfig } from '../../agg_types'; +import { IAggConfig } from '../../agg_types'; export interface AggColumn { - aggConfig: AggConfig; + aggConfig: IAggConfig; id: string; name: string; } -const getColumn = (agg: AggConfig, i: number): AggColumn => { +const getColumn = (agg: IAggConfig, i: number): AggColumn => { return { aggConfig: agg, id: `col-${i}-${agg.id}`, @@ -40,7 +40,7 @@ const getColumn = (agg: AggConfig, i: number): AggColumn => { * @param {AggConfigs} aggs - the agg configs object to which the aggregation response correlates * @param {boolean} minimalColumns - setting to true will only return a column for the last bucket/metric instead of one for each level */ -export function tabifyGetColumns(aggs: AggConfig[], minimalColumns: boolean) { +export function tabifyGetColumns(aggs: IAggConfig[], minimalColumns: boolean) { // pick the columns if (minimalColumns) { return aggs.map((agg, i) => getColumn(agg, i)); diff --git a/src/legacy/ui/public/agg_types/index.ts b/src/legacy/ui/public/agg_types/index.ts index cf2733b9a9f36..ac5d0bed7ef15 100644 --- a/src/legacy/ui/public/agg_types/index.ts +++ b/src/legacy/ui/public/agg_types/index.ts @@ -17,15 +17,69 @@ * under the License. */ -export { aggTypes } from './agg_types'; -export { AggParam } from './agg_params'; -export { AggConfig } from './agg_config'; -export { AggConfigs } from './agg_configs'; -export { AggGroupNames, aggGroupNamesMap } from './agg_groups'; -export { FieldParamType } from './param_types'; -export { BUCKET_TYPES } from './buckets/bucket_agg_types'; -export { METRIC_TYPES } from './metrics/metric_agg_types'; -export { ISchemas, Schema, Schemas } from './schemas'; -export { AggType } from './agg_type'; -export { setBounds } from './buckets/date_histogram'; -export { termsAggFilter } from './buckets/terms'; +/** + * Nothing to see here! + * + * Agg Types have moved to the data plugin, and are being + * re-exported from ui/agg_types for backwards compatibility. + */ + +import { start as dataStart } from '../../../core_plugins/data/public/legacy'; + +// runtime contracts +export const { + types: aggTypes, + AggConfig, + AggConfigs, + AggType, + aggTypeFieldFilters, + FieldParamType, + MetricAggType, + parentPipelineAggHelper, + siblingPipelineAggHelper, + setBounds, +} = dataStart.search.aggs; + +// types +export { + IAggConfig, + IAggConfigs, + IAggType, + IFieldParamType, + IMetricAggType, + AggParam, + AggParamOption, + BUCKET_TYPES, + DateRangeKey, + IpRangeKey, + ISchemas, + METRIC_TYPES, + OptionedParamEditorProps, + OptionedValueProp, +} from '../../../core_plugins/data/public'; + +// static code +export { + AggParamType, + AggTypeFilters, + aggTypeFilters, + AggTypeFieldFilters, + AggGroupNames, + aggGroupNamesMap, + CidrMask, + convertDateRangeToString, + convertIPRangeToString, + intervalOptions, + isDateHistogramBucketAggConfig, + isStringType, + isType, + isValidInterval, + isValidJson, + OptionedParamType, + parentPipelineType, + propFilter, + Schema, + Schemas, + siblingPipelineType, + termsAggFilter, +} from '../../../core_plugins/data/public'; diff --git a/src/legacy/ui/public/chrome/api/nav.ts b/src/legacy/ui/public/chrome/api/nav.ts index 771314d9e1481..ae32473e451b7 100644 --- a/src/legacy/ui/public/chrome/api/nav.ts +++ b/src/legacy/ui/public/chrome/api/nav.ts @@ -146,7 +146,7 @@ export function initChromeNavApi(chrome: any, internals: NavInternals) { // link.active and link.lastUrl properties coreNavLinks .getAll() - .filter(link => link.subUrlBase) + .filter(link => link.subUrlBase && !link.disableSubUrlTracking) .forEach(link => { coreNavLinks.update(link.id, { subUrlBase: relativeToAbsolute(chrome.addBasePath(link.subUrlBase)), diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/truncate/__snapshots__/truncate.test.js.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/truncate/__snapshots__/truncate.test.js.snap index c0954491a2a47..729487dfae5d7 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/truncate/__snapshots__/truncate.test.js.snap +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/truncate/__snapshots__/truncate.test.js.snap @@ -21,6 +21,7 @@ exports[`TruncateFormatEditor should render normally 1`] = ` > diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/truncate/truncate.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/truncate/truncate.js index fdae8627ead69..9a9b6c954b78d 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/truncate/truncate.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/truncate/truncate.js @@ -38,7 +38,7 @@ export class TruncateFormatEditor extends DefaultFormatEditor { } render() { - const { formatParams } = this.props; + const { formatParams, onError } = this.props; const { error, samples } = this.state; return ( @@ -55,8 +55,15 @@ export class TruncateFormatEditor extends DefaultFormatEditor { > { - this.onChange({ fieldLength: e.target.value ? Number(e.target.value) : null }); + if (e.target.checkValidity()) { + this.onChange({ + fieldLength: e.target.value ? Number(e.target.value) : null, + }); + } else { + onError(e.target.validationMessage); + } }} isInvalid={!!error} /> diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/truncate/truncate.test.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/truncate/truncate.test.js index e98dd4edca386..7ab6f2a9cbeb0 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/truncate/truncate.test.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/truncate/truncate.test.js @@ -19,6 +19,7 @@ import React from 'react'; import { shallow } from 'enzyme'; +import { EuiFieldNumber } from '@elastic/eui'; import { TruncateFormatEditor } from './truncate'; @@ -34,6 +35,11 @@ const onChange = jest.fn(); const onError = jest.fn(); describe('TruncateFormatEditor', () => { + beforeEach(() => { + onChange.mockClear(); + onError.mockClear(); + }); + it('should have a formatId', () => { expect(TruncateFormatEditor.formatId).toEqual('truncate'); }); @@ -50,4 +56,54 @@ describe('TruncateFormatEditor', () => { ); expect(component).toMatchSnapshot(); }); + + it('should fire error, when input is invalid', async () => { + const component = shallow( + + ); + const input = component.find(EuiFieldNumber); + + const changeEvent = { + target: { + value: '123.3', + checkValidity: () => false, + validationMessage: 'Error!', + }, + }; + await input.invoke('onChange')(changeEvent); + + expect(onError).toBeCalledWith(changeEvent.target.validationMessage); + expect(onChange).not.toBeCalled(); + }); + + it('should fire change, when input changed and is valid', async () => { + const component = shallow( + + ); + const input = component.find(EuiFieldNumber); + + const changeEvent = { + target: { + value: '123', + checkValidity: () => true, + validationMessage: null, + }, + }; + onError.mockClear(); + await input.invoke('onChange')(changeEvent); + expect(onError).not.toBeCalled(); + expect(onChange).toBeCalledWith({ fieldLength: 123 }); + }); }); diff --git a/src/legacy/ui/public/time_buckets/time_buckets.js b/src/legacy/ui/public/time_buckets/time_buckets.js index 50a57d866099e..a611de45fa859 100644 --- a/src/legacy/ui/public/time_buckets/time_buckets.js +++ b/src/legacy/ui/public/time_buckets/time_buckets.js @@ -144,7 +144,7 @@ TimeBuckets.prototype.getDuration = function() { * generated. * * Input can be one of the following: - * - Any object from src/legacy/ui/agg_types/buckets/_interval_options.js + * - Any object from src/legacy/ui/agg_types.js * - "auto" * - Pass a valid moment unit * - a moment.duration object. diff --git a/src/legacy/ui/public/vis/__tests__/_agg_config.js b/src/legacy/ui/public/vis/__tests__/_agg_config.js index 2ccbaf6c1645e..7dccf3eec18aa 100644 --- a/src/legacy/ui/public/vis/__tests__/_agg_config.js +++ b/src/legacy/ui/public/vis/__tests__/_agg_config.js @@ -21,8 +21,8 @@ import sinon from 'sinon'; import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import { Vis } from '../../../../core_plugins/visualizations/public'; -import { AggType } from '../../agg_types/agg_type'; -import { AggConfig } from '../../agg_types/agg_config'; +import { AggType, AggConfig } from '../../agg_types'; + import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; describe('AggConfig', function() { 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/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts b/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts index e763eb1b90791..d8227343159e6 100644 --- a/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts +++ b/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts @@ -19,15 +19,15 @@ import { i18n } from '@kbn/i18n'; import { identity } from 'lodash'; -import { AggConfig } from 'ui/agg_types'; +import { IAggConfig } from 'ui/agg_types'; import { npStart } from 'ui/new_platform'; import { SerializedFieldFormat } from 'src/plugins/expressions/public'; import { fieldFormats } from '../../../../../../plugins/data/public'; import { Vis } from '../../../../../core_plugins/visualizations/public'; import { tabifyGetColumns } from '../../../agg_response/tabify/_get_columns'; -import { DateRangeKey, convertDateRangeToString } from '../../../agg_types/buckets/date_range'; -import { IpRangeKey, convertIPRangeToString } from '../../../agg_types/buckets/ip_range'; +import { DateRangeKey, convertDateRangeToString } from '../../../agg_types'; +import { IpRangeKey, convertIPRangeToString } from '../../../agg_types'; interface TermsFieldFormatParams { otherBucketLabel: string; @@ -62,7 +62,7 @@ const getFieldFormat = ( return new DefaultFieldFormat(); }; -export const createFormat = (agg: AggConfig): SerializedFieldFormat => { +export const createFormat = (agg: IAggConfig): SerializedFieldFormat => { const format: SerializedFieldFormat = agg.params.field ? agg.params.field.format.toJSON() : {}; const formats: Record SerializedFieldFormat> = { date_range: () => ({ id: 'date_range', params: format }), @@ -106,7 +106,7 @@ export const getFormat: FormatFactory = mapping => { const format = getFieldFormat(id, mapping.params); const gte = '\u2265'; const lt = '\u003c'; - return i18n.translate('common.ui.aggTypes.buckets.ranges.rangesFormatMessage', { + return i18n.translate('common.ui.aggTypes.rangesFormatMessage', { defaultMessage: '{gte} {from} and {lt} {to}', values: { gte, @@ -155,7 +155,7 @@ export const getFormat: FormatFactory = mapping => { } }; -export const getTableAggs = (vis: Vis): AggConfig[] => { +export const getTableAggs = (vis: Vis): IAggConfig[] => { if (!vis.aggs || !vis.aggs.getResponseAggs) { return []; } diff --git a/src/plugins/data/common/field_formats/converters/truncate.test.ts b/src/plugins/data/common/field_formats/converters/truncate.test.ts index 472d9673346d7..3a0abc918fa98 100644 --- a/src/plugins/data/common/field_formats/converters/truncate.test.ts +++ b/src/plugins/data/common/field_formats/converters/truncate.test.ts @@ -43,4 +43,11 @@ describe('String TruncateFormat', () => { expect(truncate.convert('This is some text')).toBe('This is some text'); }); + + test('does not truncate whole text when non integer is passed in', () => { + // https://github.com/elastic/kibana/issues/29648 + const truncate = new TruncateFormat({ fieldLength: 3.2 }, jest.fn()); + + expect(truncate.convert('This is some text')).toBe('Thi...'); + }); }); diff --git a/src/plugins/data/public/index_patterns/index.ts b/src/plugins/data/public/index_patterns/index.ts index 7444126ee6cae..ecddd893d1a54 100644 --- a/src/plugins/data/public/index_patterns/index.ts +++ b/src/plugins/data/public/index_patterns/index.ts @@ -44,7 +44,13 @@ export const indexPatterns = { isDefault, }; -export { Field, FieldList, IFieldList } from './fields'; +export { Field, FieldList } 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..b2060dd1d48ba --- /dev/null +++ b/src/plugins/data/public/index_patterns/index_patterns/types.ts @@ -0,0 +1,35 @@ +/* + * 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; + [key: string]: any; +} diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index 847d79fdc87d1..726cd6cfb18f0 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -101,6 +101,8 @@ const createStartContract = (): Start => { return startContract; }; +export { searchSourceMock } from './search/mocks'; + export const dataPluginMock = { createSetupContract, createStartContract, diff --git a/src/plugins/data/public/query/state_sync/index.ts b/src/plugins/data/public/query/state_sync/index.ts index 7eefda0d0aec1..27e02940765cf 100644 --- a/src/plugins/data/public/query/state_sync/index.ts +++ b/src/plugins/data/public/query/state_sync/index.ts @@ -17,5 +17,5 @@ * under the License. */ -export { syncQuery } from './sync_query'; +export { syncQuery, getQueryStateContainer } from './sync_query'; export { syncAppFilters } from './sync_app_filters'; diff --git a/src/plugins/data/public/query/state_sync/sync_query.test.ts b/src/plugins/data/public/query/state_sync/sync_query.test.ts index 0973af13cacd5..4796da4f5fd4b 100644 --- a/src/plugins/data/public/query/state_sync/sync_query.test.ts +++ b/src/plugins/data/public/query/state_sync/sync_query.test.ts @@ -31,7 +31,7 @@ import { import { QueryService, QueryStart } from '../query_service'; import { StubBrowserStorage } from 'test_utils/stub_browser_storage'; import { TimefilterContract } from '../timefilter'; -import { QuerySyncState, syncQuery } from './sync_query'; +import { getQueryStateContainer, QuerySyncState, syncQuery } from './sync_query'; const setupMock = coreMock.createSetup(); const startMock = coreMock.createStart(); @@ -163,4 +163,69 @@ describe('sync_query', () => { expect(spy).not.toBeCalled(); stop(); }); + + describe('getQueryStateContainer', () => { + test('state is initialized with state from query service', () => { + const { stop, querySyncStateContainer, initialState } = getQueryStateContainer( + queryServiceStart + ); + expect(querySyncStateContainer.getState()).toMatchInlineSnapshot(` + Object { + "filters": Array [], + "refreshInterval": Object { + "pause": true, + "value": 0, + }, + "time": Object { + "from": "now-15m", + "to": "now", + }, + } + `); + expect(initialState).toEqual(querySyncStateContainer.getState()); + stop(); + }); + + test('state takes initial overrides into account', () => { + const { stop, querySyncStateContainer, initialState } = getQueryStateContainer( + queryServiceStart, + { + time: { from: 'now-99d', to: 'now' }, + } + ); + expect(querySyncStateContainer.getState().time).toEqual({ + from: 'now-99d', + to: 'now', + }); + expect(initialState).toEqual(querySyncStateContainer.getState()); + stop(); + }); + + test('when filters change, state container contains updated global filters', () => { + const { stop, querySyncStateContainer } = getQueryStateContainer(queryServiceStart); + filterManager.setFilters([gF, aF]); + expect(querySyncStateContainer.getState().filters).toHaveLength(1); + stop(); + }); + + test('when time range changes, state container contains updated time range', () => { + const { stop, querySyncStateContainer } = getQueryStateContainer(queryServiceStart); + timefilter.setTime({ from: 'now-30m', to: 'now' }); + expect(querySyncStateContainer.getState().time).toEqual({ + from: 'now-30m', + to: 'now', + }); + stop(); + }); + + test('when refresh interval changes, state container contains updated refresh interval', () => { + const { stop, querySyncStateContainer } = getQueryStateContainer(queryServiceStart); + timefilter.setRefreshInterval({ pause: true, value: 100 }); + expect(querySyncStateContainer.getState().refreshInterval).toEqual({ + pause: true, + value: 100, + }); + stop(); + }); + }); }); diff --git a/src/plugins/data/public/query/state_sync/sync_query.ts b/src/plugins/data/public/query/state_sync/sync_query.ts index be641e89f9b76..9a4e9cbba2990 100644 --- a/src/plugins/data/public/query/state_sync/sync_query.ts +++ b/src/plugins/data/public/query/state_sync/sync_query.ts @@ -27,7 +27,7 @@ import { } from '../../../../kibana_utils/public'; import { COMPARE_ALL_OPTIONS, compareFilters } from '../filter_manager/lib/compare_filters'; import { esFilters, RefreshInterval, TimeRange } from '../../../common'; -import { QueryStart } from '../query_service'; +import { QuerySetup, QueryStart } from '../query_service'; const GLOBAL_STATE_STORAGE_KEY = '_g'; @@ -40,16 +40,11 @@ export interface QuerySyncState { /** * Helper utility to set up syncing between query services and url's '_g' query param */ -export const syncQuery = ( - { timefilter: { timefilter }, filterManager }: QueryStart, - urlStateStorage: IKbnUrlStateStorage -) => { - const defaultState: QuerySyncState = { - time: timefilter.getTime(), - refreshInterval: timefilter.getRefreshInterval(), - filters: filterManager.getGlobalFilters(), - }; - +export const syncQuery = (queryStart: QueryStart, urlStateStorage: IKbnUrlStateStorage) => { + const { + timefilter: { timefilter }, + filterManager, + } = queryStart; // retrieve current state from `_g` url const initialStateFromUrl = urlStateStorage.get(GLOBAL_STATE_STORAGE_KEY); @@ -58,10 +53,82 @@ export const syncQuery = ( initialStateFromUrl && Object.keys(initialStateFromUrl).length ); - // prepare initial state, whatever was in URL takes precedences over current state in services + const { + querySyncStateContainer, + stop: stopPullQueryState, + initialState, + } = getQueryStateContainer(queryStart, initialStateFromUrl || {}); + + const pushQueryStateSubscription = querySyncStateContainer.state$.subscribe( + ({ time, filters: globalFilters, refreshInterval }) => { + // cloneDeep is required because services are mutating passed objects + // and state in state container is frozen + if (time && !_.isEqual(time, timefilter.getTime())) { + timefilter.setTime(_.cloneDeep(time)); + } + + if (refreshInterval && !_.isEqual(refreshInterval, timefilter.getRefreshInterval())) { + timefilter.setRefreshInterval(_.cloneDeep(refreshInterval)); + } + + if ( + globalFilters && + !compareFilters(globalFilters, filterManager.getGlobalFilters(), COMPARE_ALL_OPTIONS) + ) { + filterManager.setGlobalFilters(_.cloneDeep(globalFilters)); + } + } + ); + + // if there weren't any initial state in url, + // then put _g key into url + if (!initialStateFromUrl) { + urlStateStorage.set(GLOBAL_STATE_STORAGE_KEY, initialState, { + replace: true, + }); + } + + // trigger initial syncing from state container to services if needed + querySyncStateContainer.set(initialState); + + const { start, stop: stopSyncState } = syncState({ + stateStorage: urlStateStorage, + stateContainer: { + ...querySyncStateContainer, + set: state => { + if (state) { + // syncState utils requires to handle incoming "null" value + querySyncStateContainer.set(state); + } + }, + }, + storageKey: GLOBAL_STATE_STORAGE_KEY, + }); + + start(); + return { + stop: () => { + stopSyncState(); + pushQueryStateSubscription.unsubscribe(); + stopPullQueryState(); + }, + hasInheritedQueryFromUrl, + }; +}; + +export const getQueryStateContainer = ( + { timefilter: { timefilter }, filterManager }: QuerySetup, + initialStateOverrides: Partial = {} +) => { + const defaultState: QuerySyncState = { + time: timefilter.getTime(), + refreshInterval: timefilter.getRefreshInterval(), + filters: filterManager.getGlobalFilters(), + }; + const initialState: QuerySyncState = { ...defaultState, - ...initialStateFromUrl, + ...initialStateOverrides, }; // create state container, which will be used for syncing with syncState() util @@ -109,59 +176,13 @@ export const syncQuery = ( .subscribe(newGlobalFilters => { querySyncStateContainer.transitions.setFilters(newGlobalFilters); }), - querySyncStateContainer.state$.subscribe( - ({ time, filters: globalFilters, refreshInterval }) => { - // cloneDeep is required because services are mutating passed objects - // and state in state container is frozen - if (time && !_.isEqual(time, timefilter.getTime())) { - timefilter.setTime(_.cloneDeep(time)); - } - - if (refreshInterval && !_.isEqual(refreshInterval, timefilter.getRefreshInterval())) { - timefilter.setRefreshInterval(_.cloneDeep(refreshInterval)); - } - - if ( - globalFilters && - !compareFilters(globalFilters, filterManager.getGlobalFilters(), COMPARE_ALL_OPTIONS) - ) { - filterManager.setGlobalFilters(_.cloneDeep(globalFilters)); - } - } - ), ]; - // if there weren't any initial state in url, - // then put _g key into url - if (!initialStateFromUrl) { - urlStateStorage.set(GLOBAL_STATE_STORAGE_KEY, initialState, { - replace: true, - }); - } - - // trigger initial syncing from state container to services if needed - querySyncStateContainer.set(initialState); - - const { start, stop } = syncState({ - stateStorage: urlStateStorage, - stateContainer: { - ...querySyncStateContainer, - set: state => { - if (state) { - // syncState utils requires to handle incoming "null" value - querySyncStateContainer.set(state); - } - }, - }, - storageKey: GLOBAL_STATE_STORAGE_KEY, - }); - - start(); return { + querySyncStateContainer, stop: () => { subs.forEach(s => s.unsubscribe()); - stop(); }, - hasInheritedQueryFromUrl, + initialState, }; }; diff --git a/src/plugins/data/public/search/mocks.ts b/src/plugins/data/public/search/mocks.ts index 81a028007bc94..821bd45f731e8 100644 --- a/src/plugins/data/public/search/mocks.ts +++ b/src/plugins/data/public/search/mocks.ts @@ -17,6 +17,8 @@ * under the License. */ +export * from './search_source/mocks'; + export const searchSetupMock = { registerSearchStrategyContext: jest.fn(), registerSearchStrategyProvider: jest.fn(), diff --git a/src/plugins/home/server/tutorials/instructions/instruction_variant.ts b/src/plugins/home/common/instruction_variant.ts similarity index 100% rename from src/plugins/home/server/tutorials/instructions/instruction_variant.ts rename to src/plugins/home/common/instruction_variant.ts diff --git a/src/plugins/home/public/index.ts b/src/plugins/home/public/index.ts index 114d442b40943..2a445cf242729 100644 --- a/src/plugins/home/public/index.ts +++ b/src/plugins/home/public/index.ts @@ -26,6 +26,7 @@ export { HomePublicPluginStart, } from './plugin'; export { FeatureCatalogueEntry, FeatureCatalogueCategory, Environment } from './services'; +export * from '../common/instruction_variant'; import { HomePublicPlugin } from './plugin'; export const plugin = (initializerContext: PluginInitializerContext) => diff --git a/src/plugins/home/server/index.ts b/src/plugins/home/server/index.ts index 02f4c91a414cc..75ace84344216 100644 --- a/src/plugins/home/server/index.ts +++ b/src/plugins/home/server/index.ts @@ -36,5 +36,5 @@ export const config: PluginConfigDescriptor = { export const plugin = (initContext: PluginInitializerContext) => new HomeServerPlugin(initContext); -export { INSTRUCTION_VARIANT } from './tutorials/instructions/instruction_variant'; +export { INSTRUCTION_VARIANT } from '../common/instruction_variant'; export { ArtifactsSchema, TutorialsCategory } from './services/tutorials'; diff --git a/src/plugins/home/server/tutorials/instructions/auditbeat_instructions.ts b/src/plugins/home/server/tutorials/instructions/auditbeat_instructions.ts index 6a9dba69b193f..4c85ad3985b3d 100644 --- a/src/plugins/home/server/tutorials/instructions/auditbeat_instructions.ts +++ b/src/plugins/home/server/tutorials/instructions/auditbeat_instructions.ts @@ -18,7 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -import { INSTRUCTION_VARIANT } from './instruction_variant'; +import { INSTRUCTION_VARIANT } from '../../../common/instruction_variant'; import { createTrycloudOption1, createTrycloudOption2 } from './onprem_cloud_instructions'; import { getSpaceIdForBeatsTutorial } from './get_space_id_for_beats_tutorial'; import { Platform, TutorialContext } from '../../services/tutorials/lib/tutorials_registry_types'; diff --git a/src/plugins/home/server/tutorials/instructions/filebeat_instructions.ts b/src/plugins/home/server/tutorials/instructions/filebeat_instructions.ts index 176a3901821f1..66efa36ec9bcd 100644 --- a/src/plugins/home/server/tutorials/instructions/filebeat_instructions.ts +++ b/src/plugins/home/server/tutorials/instructions/filebeat_instructions.ts @@ -18,7 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -import { INSTRUCTION_VARIANT } from './instruction_variant'; +import { INSTRUCTION_VARIANT } from '../../../common/instruction_variant'; import { createTrycloudOption1, createTrycloudOption2 } from './onprem_cloud_instructions'; import { getSpaceIdForBeatsTutorial } from './get_space_id_for_beats_tutorial'; import { Platform, TutorialContext } from '../../services/tutorials/lib/tutorials_registry_types'; diff --git a/src/plugins/home/server/tutorials/instructions/functionbeat_instructions.ts b/src/plugins/home/server/tutorials/instructions/functionbeat_instructions.ts index 385880ba9780f..ee13b9c5eefd8 100644 --- a/src/plugins/home/server/tutorials/instructions/functionbeat_instructions.ts +++ b/src/plugins/home/server/tutorials/instructions/functionbeat_instructions.ts @@ -18,7 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -import { INSTRUCTION_VARIANT } from './instruction_variant'; +import { INSTRUCTION_VARIANT } from '../../../common/instruction_variant'; import { createTrycloudOption1, createTrycloudOption2 } from './onprem_cloud_instructions'; import { getSpaceIdForBeatsTutorial } from './get_space_id_for_beats_tutorial'; import { Platform, TutorialContext } from '../../services/tutorials/lib/tutorials_registry_types'; diff --git a/src/plugins/home/server/tutorials/instructions/heartbeat_instructions.ts b/src/plugins/home/server/tutorials/instructions/heartbeat_instructions.ts index 406bf55da4321..33f5defc0273f 100644 --- a/src/plugins/home/server/tutorials/instructions/heartbeat_instructions.ts +++ b/src/plugins/home/server/tutorials/instructions/heartbeat_instructions.ts @@ -18,7 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -import { INSTRUCTION_VARIANT } from './instruction_variant'; +import { INSTRUCTION_VARIANT } from '../../../common/instruction_variant'; import { createTrycloudOption1, createTrycloudOption2 } from './onprem_cloud_instructions'; import { getSpaceIdForBeatsTutorial } from './get_space_id_for_beats_tutorial'; import { Platform, TutorialContext } from '../../services/tutorials/lib/tutorials_registry_types'; diff --git a/src/plugins/home/server/tutorials/instructions/metricbeat_instructions.ts b/src/plugins/home/server/tutorials/instructions/metricbeat_instructions.ts index 77efe0958a615..9fdc70e0703a4 100644 --- a/src/plugins/home/server/tutorials/instructions/metricbeat_instructions.ts +++ b/src/plugins/home/server/tutorials/instructions/metricbeat_instructions.ts @@ -18,7 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -import { INSTRUCTION_VARIANT } from './instruction_variant'; +import { INSTRUCTION_VARIANT } from '../../../common/instruction_variant'; import { createTrycloudOption1, createTrycloudOption2 } from './onprem_cloud_instructions'; import { getSpaceIdForBeatsTutorial } from './get_space_id_for_beats_tutorial'; import { TutorialContext } from '../../services/tutorials/lib/tutorials_registry_types'; diff --git a/src/plugins/home/server/tutorials/instructions/winlogbeat_instructions.ts b/src/plugins/home/server/tutorials/instructions/winlogbeat_instructions.ts index cc18f2ce9705d..9d7d0660d3d6c 100644 --- a/src/plugins/home/server/tutorials/instructions/winlogbeat_instructions.ts +++ b/src/plugins/home/server/tutorials/instructions/winlogbeat_instructions.ts @@ -18,7 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -import { INSTRUCTION_VARIANT } from './instruction_variant'; +import { INSTRUCTION_VARIANT } from '../../../common/instruction_variant'; import { createTrycloudOption1, createTrycloudOption2 } from './onprem_cloud_instructions'; import { getSpaceIdForBeatsTutorial } from './get_space_id_for_beats_tutorial'; import { TutorialContext } from '../../services/tutorials/lib/tutorials_registry_types'; diff --git a/src/plugins/home/server/tutorials/netflow/elastic_cloud.ts b/src/plugins/home/server/tutorials/netflow/elastic_cloud.ts index ac64aef730004..fbedc6abfbb8a 100644 --- a/src/plugins/home/server/tutorials/netflow/elastic_cloud.ts +++ b/src/plugins/home/server/tutorials/netflow/elastic_cloud.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; -import { INSTRUCTION_VARIANT } from '../instructions/instruction_variant'; +import { INSTRUCTION_VARIANT } from '../../../common/instruction_variant'; import { createLogstashInstructions } from '../instructions/logstash_instructions'; import { createCommonNetflowInstructions } from './common_instructions'; diff --git a/src/plugins/home/server/tutorials/netflow/on_prem.ts b/src/plugins/home/server/tutorials/netflow/on_prem.ts index c7cd36d073632..ef8c3e172af87 100644 --- a/src/plugins/home/server/tutorials/netflow/on_prem.ts +++ b/src/plugins/home/server/tutorials/netflow/on_prem.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; -import { INSTRUCTION_VARIANT } from '../instructions/instruction_variant'; +import { INSTRUCTION_VARIANT } from '../../../common/instruction_variant'; import { createLogstashInstructions } from '../instructions/logstash_instructions'; import { createCommonNetflowInstructions } from './common_instructions'; diff --git a/src/plugins/home/server/tutorials/netflow/on_prem_elastic_cloud.ts b/src/plugins/home/server/tutorials/netflow/on_prem_elastic_cloud.ts index c01a9a5382f88..85aa694970491 100644 --- a/src/plugins/home/server/tutorials/netflow/on_prem_elastic_cloud.ts +++ b/src/plugins/home/server/tutorials/netflow/on_prem_elastic_cloud.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; -import { INSTRUCTION_VARIANT } from '../instructions/instruction_variant'; +import { INSTRUCTION_VARIANT } from '../../../common/instruction_variant'; import { createLogstashInstructions } from '../instructions/logstash_instructions'; import { createTrycloudOption1, diff --git a/src/plugins/kibana_legacy/public/plugin.ts b/src/plugins/kibana_legacy/public/plugin.ts index b9a61a1c9b200..7c4b3428cbb6d 100644 --- a/src/plugins/kibana_legacy/public/plugin.ts +++ b/src/plugins/kibana_legacy/public/plugin.ts @@ -17,8 +17,8 @@ * under the License. */ -import { App, PluginInitializerContext } from 'kibana/public'; - +import { App, AppBase, PluginInitializerContext, AppUpdatableFields } from 'kibana/public'; +import { Observable } from 'rxjs'; import { ConfigSchema } from '../config'; interface ForwardDefinition { @@ -27,8 +27,26 @@ interface ForwardDefinition { keepPrefix: boolean; } +export type AngularRenderedAppUpdater = ( + app: AppBase +) => Partial | undefined; + +export interface AngularRenderedApp extends App { + /** + * Angular rendered apps are able to update the active url in the nav link (which is currently not + * possible for actual NP apps). When regular applications have the same functionality, this type + * override can be removed. + */ + updater$?: Observable; + /** + * If the active url is updated via the updater$ subject, the app id is assumed to be identical with + * the nav link id. If this is not the case, it is possible to provide another nav link id here. + */ + navLinkId?: string; +} + export class KibanaLegacyPlugin { - private apps: App[] = []; + private apps: AngularRenderedApp[] = []; private forwards: ForwardDefinition[] = []; constructor(private readonly initializerContext: PluginInitializerContext) {} @@ -52,7 +70,7 @@ export class KibanaLegacyPlugin { * * @param app The app descriptor */ - registerLegacyApp: (app: App) => { + registerLegacyApp: (app: AngularRenderedApp) => { this.apps.push(app); }, diff --git a/src/plugins/kibana_react/public/adapters/index.ts b/src/plugins/kibana_react/public/adapters/index.ts new file mode 100644 index 0000000000000..9912967022793 --- /dev/null +++ b/src/plugins/kibana_react/public/adapters/index.ts @@ -0,0 +1,21 @@ +/* + * 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 * from './react_to_ui_component'; +export * from './ui_to_react_component'; diff --git a/src/plugins/kibana_react/public/adapters/react_to_ui_component.test.tsx b/src/plugins/kibana_react/public/adapters/react_to_ui_component.test.tsx new file mode 100644 index 0000000000000..14fe02c6bf652 --- /dev/null +++ b/src/plugins/kibana_react/public/adapters/react_to_ui_component.test.tsx @@ -0,0 +1,86 @@ +/* + * 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 * as React from 'react'; +import { reactToUiComponent } from './react_to_ui_component'; + +const ReactComp: React.FC<{ cnt?: number }> = ({ cnt = 0 }) => { + return
cnt: {cnt}
; +}; + +describe('reactToUiComponent', () => { + test('can render UI component', () => { + const UiComp = reactToUiComponent(ReactComp); + const div = document.createElement('div'); + + const instance = UiComp(); + instance.render(div, {}); + + expect(div.innerHTML).toBe('
cnt: 0
'); + }); + + test('can pass in props', async () => { + const UiComp = reactToUiComponent(ReactComp); + const div = document.createElement('div'); + + const instance = UiComp(); + instance.render(div, { cnt: 5 }); + + expect(div.innerHTML).toBe('
cnt: 5
'); + }); + + test('can re-render multiple times', async () => { + const UiComp = reactToUiComponent(ReactComp); + const div = document.createElement('div'); + const instance = UiComp(); + + instance.render(div, { cnt: 1 }); + + expect(div.innerHTML).toBe('
cnt: 1
'); + + instance.render(div, { cnt: 2 }); + + expect(div.innerHTML).toBe('
cnt: 2
'); + }); + + test('renders React component only when .render() method is called', () => { + let renderCnt = 0; + const MyReactComp: React.FC<{ cnt?: number }> = ({ cnt = 0 }) => { + renderCnt++; + return
cnt: {cnt}
; + }; + const UiComp = reactToUiComponent(MyReactComp); + const instance = UiComp(); + const div = document.createElement('div'); + + expect(renderCnt).toBe(0); + + instance.render(div, { cnt: 1 }); + + expect(renderCnt).toBe(1); + + instance.render(div, { cnt: 2 }); + + expect(renderCnt).toBe(2); + + instance.render(div, { cnt: 3 }); + + expect(renderCnt).toBe(3); + }); +}); diff --git a/src/plugins/kibana_react/public/adapters/react_to_ui_component.ts b/src/plugins/kibana_react/public/adapters/react_to_ui_component.ts new file mode 100644 index 0000000000000..b4007b30cf8ca --- /dev/null +++ b/src/plugins/kibana_react/public/adapters/react_to_ui_component.ts @@ -0,0 +1,49 @@ +/* + * 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 { ComponentType, createElement as h } from 'react'; +import { render as renderReact, unmountComponentAtNode } from 'react-dom'; +import { UiComponent, UiComponentInstance } from '../../../kibana_utils/common'; + +/** + * Transform a React component into a `UiComponent`. + * + * @param ReactComp A React component. + */ +export const reactToUiComponent = ( + ReactComp: ComponentType +): UiComponent => () => { + let lastEl: HTMLElement | undefined; + + const render: UiComponentInstance['render'] = (el, props) => { + lastEl = el; + renderReact(h(ReactComp, props), el); + }; + + const unmount: UiComponentInstance['unmount'] = () => { + if (lastEl) unmountComponentAtNode(lastEl); + }; + + const comp: UiComponentInstance = { + render, + unmount, + }; + + return comp; +}; diff --git a/src/plugins/kibana_react/public/adapters/ui_to_react_component.test.tsx b/src/plugins/kibana_react/public/adapters/ui_to_react_component.test.tsx new file mode 100644 index 0000000000000..939d372b9997f --- /dev/null +++ b/src/plugins/kibana_react/public/adapters/ui_to_react_component.test.tsx @@ -0,0 +1,149 @@ +/* + * 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 * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { UiComponent } from '../../../kibana_utils/common'; +import { uiToReactComponent } from './ui_to_react_component'; +import { reactToUiComponent } from './react_to_ui_component'; + +const UiComp: UiComponent<{ cnt?: number }> = () => ({ + render: (el, { cnt = 0 }) => { + el.innerHTML = `cnt: ${cnt}`; + }, +}); + +describe('uiToReactComponent', () => { + test('can render React component', () => { + const ReactComp = uiToReactComponent(UiComp); + const div = document.createElement('div'); + + ReactDOM.render(, div); + + expect(div.innerHTML).toBe('
cnt: 0
'); + }); + + test('can pass in props', async () => { + const ReactComp = uiToReactComponent(UiComp); + const div = document.createElement('div'); + + ReactDOM.render(, div); + + expect(div.innerHTML).toBe('
cnt: 5
'); + }); + + test('re-renders when React component is re-rendered', async () => { + const ReactComp = uiToReactComponent(UiComp); + const div = document.createElement('div'); + + ReactDOM.render(, div); + + expect(div.innerHTML).toBe('
cnt: 1
'); + + ReactDOM.render(, div); + + expect(div.innerHTML).toBe('
cnt: 2
'); + }); + + test('does not crash if .unmount() not provided', () => { + const UiComp2: UiComponent<{ cnt?: number }> = () => ({ + render: (el, { cnt = 0 }) => { + el.innerHTML = `cnt: ${cnt}`; + }, + }); + const ReactComp = uiToReactComponent(UiComp2); + const div = document.createElement('div'); + + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); + + expect(div.innerHTML).toBe(''); + }); + + test('calls .unmount() method once when component un-mounts', () => { + const unmount = jest.fn(); + const UiComp2: UiComponent<{ cnt?: number }> = () => ({ + render: (el, { cnt = 0 }) => { + el.innerHTML = `cnt: ${cnt}`; + }, + unmount, + }); + const ReactComp = uiToReactComponent(UiComp2); + const div = document.createElement('div'); + + expect(unmount).toHaveBeenCalledTimes(0); + + ReactDOM.render(, div); + + expect(unmount).toHaveBeenCalledTimes(0); + + ReactDOM.unmountComponentAtNode(div); + + expect(unmount).toHaveBeenCalledTimes(1); + }); + + test('calls .render() method only once when components mounts, and once on every re-render', () => { + const render = jest.fn((el, { cnt = 0 }) => { + el.innerHTML = `cnt: ${cnt}`; + }); + const UiComp2: UiComponent<{ cnt?: number }> = () => ({ + render, + }); + const ReactComp = uiToReactComponent(UiComp2); + const div = document.createElement('div'); + + expect(render).toHaveBeenCalledTimes(0); + + ReactDOM.render(, div); + + expect(render).toHaveBeenCalledTimes(1); + + ReactDOM.render(, div); + + expect(render).toHaveBeenCalledTimes(2); + + ReactDOM.render(, div); + + expect(render).toHaveBeenCalledTimes(3); + }); + + test('can specify wrapper element', async () => { + const ReactComp = uiToReactComponent(UiComp, 'span'); + const div = document.createElement('div'); + + ReactDOM.render(, div); + + expect(div.innerHTML).toBe('cnt: 5'); + }); +}); + +test('can adapt component many times', () => { + const ReactComp = uiToReactComponent( + reactToUiComponent(uiToReactComponent(reactToUiComponent(uiToReactComponent(UiComp)))) + ); + const div = document.createElement('div'); + + ReactDOM.render(, div); + + expect(div.textContent).toBe('cnt: 0'); + + ReactDOM.render(, div); + + expect(div.textContent).toBe('cnt: 123'); +}); diff --git a/src/plugins/kibana_react/public/adapters/ui_to_react_component.ts b/src/plugins/kibana_react/public/adapters/ui_to_react_component.ts new file mode 100644 index 0000000000000..9b34880cf4fe3 --- /dev/null +++ b/src/plugins/kibana_react/public/adapters/ui_to_react_component.ts @@ -0,0 +1,48 @@ +/* + * 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 { FC, createElement as h, useRef, useLayoutEffect, useMemo } from 'react'; +import { UiComponent, UiComponentInstance } from '../../../kibana_utils/common'; + +/** + * Transforms `UiComponent` into a React component. + */ +export const uiToReactComponent = ( + Comp: UiComponent, + as: string = 'div' +): FC => props => { + const ref = useRef(); + const comp = useMemo>(() => Comp(), [Comp]); + + useLayoutEffect(() => { + if (!ref.current) return; + comp.render(ref.current, props); + }); + + useLayoutEffect(() => { + if (!comp.unmount) return; + return () => { + if (comp.unmount) comp.unmount(); + }; + }, [comp]); + + return h(as, { + ref, + }); +}; diff --git a/src/plugins/kibana_react/public/index.ts b/src/plugins/kibana_react/public/index.ts index 81f2e694e8e5b..e1f90b9c60199 100644 --- a/src/plugins/kibana_react/public/index.ts +++ b/src/plugins/kibana_react/public/index.ts @@ -26,5 +26,6 @@ export * from './ui_settings'; export * from './field_icon'; export * from './table_list_view'; export * from './split_panel'; +export { reactToUiComponent, uiToReactComponent } from './adapters'; export { useUrlTracker } from './use_url_tracker'; export { toMountPoint } from './util'; diff --git a/src/plugins/kibana_utils/common/index.ts b/src/plugins/kibana_utils/common/index.ts index 444c10194a8e3..fb608a0db1ac2 100644 --- a/src/plugins/kibana_utils/common/index.ts +++ b/src/plugins/kibana_utils/common/index.ts @@ -19,6 +19,7 @@ export * from './defer'; export * from './of'; +export * from './ui'; export * from './state_containers'; export { createGetterSetter, Get, Set } from './create_getter_setter'; export { distinctUntilChangedWithInitialValue } from './distinct_until_changed_with_initial_value'; diff --git a/src/plugins/kibana_utils/common/ui/index.ts b/src/plugins/kibana_utils/common/ui/index.ts new file mode 100644 index 0000000000000..0cfb2f13c8a5d --- /dev/null +++ b/src/plugins/kibana_utils/common/ui/index.ts @@ -0,0 +1,20 @@ +/* + * 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 * from './ui_component'; diff --git a/src/plugins/kibana_utils/common/ui/ui_component.ts b/src/plugins/kibana_utils/common/ui/ui_component.ts new file mode 100644 index 0000000000000..6984ab9b78e5f --- /dev/null +++ b/src/plugins/kibana_utils/common/ui/ui_component.ts @@ -0,0 +1,51 @@ +/* + * 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. + */ + +/** + * In many places in Kibana we want to be agnostic to frontend view library, + * i.e. instead of exposing React-specific APIs we want to expose APIs that + * are orthogonal to any rendering library. This interface represents such UI + * components. UI component receives a DOM element and `props` through `render()` + * method, the `render()` method can be called many times. + * + * Although Kibana aims to be library agnostic, Kibana itself is written in React, + * thus here we define `UiComponent` which is an abstract unit of UI that can be + * implemented in any framework, but it maps easily to React components, i.e. + * `UiComponent` is like `React.ComponentType`. + */ +export type UiComponent = () => UiComponentInstance; + +/** + * Instance of an UiComponent, corresponds to React virtual DOM node. + */ +export interface UiComponentInstance { + /** + * Call this method on initial render and on all subsequent updates. + * + * @param el DOM element. + * @param props Component props, same as props in React. + */ + render(el: HTMLElement, props: Props): void; + + /** + * Un-mount UI component. Call it to remove view from DOM. Implementers of this + * interface should clear DOM from this UI component and destroy any internal state. + */ + unmount?(): void; +} diff --git a/src/plugins/kibana_utils/public/index.ts b/src/plugins/kibana_utils/public/index.ts index 5b6d304e14c2e..883f28da45223 100644 --- a/src/plugins/kibana_utils/public/index.ts +++ b/src/plugins/kibana_utils/public/index.ts @@ -17,7 +17,16 @@ * under the License. */ -export { defer, Defer, of, createGetterSetter, Get, Set } from '../common'; +export { + defer, + Defer, + of, + createGetterSetter, + Get, + Set, + UiComponent, + UiComponentInstance, +} from '../common'; export * from './core'; export * from './errors'; export * from './field_mapping'; @@ -40,6 +49,7 @@ export { unhashUrl, unhashQuery, createUrlTracker, + createKbnUrlTracker, createKbnUrlControls, getStateFromKbnUrl, getStatesFromKbnUrl, diff --git a/src/plugins/kibana_utils/public/state_management/url/index.ts b/src/plugins/kibana_utils/public/state_management/url/index.ts index 40491bf7a274b..e28d183c6560a 100644 --- a/src/plugins/kibana_utils/public/state_management/url/index.ts +++ b/src/plugins/kibana_utils/public/state_management/url/index.ts @@ -25,4 +25,5 @@ export { getStatesFromKbnUrl, IKbnUrlControls, } from './kbn_url_storage'; +export { createKbnUrlTracker } from './kbn_url_tracker'; export { createUrlTracker } from './url_tracker'; diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.test.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.test.ts new file mode 100644 index 0000000000000..4b17d8517328b --- /dev/null +++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.test.ts @@ -0,0 +1,184 @@ +/* + * 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 { StubBrowserStorage } from 'test_utils/stub_browser_storage'; +import { createMemoryHistory, History } from 'history'; +import { createKbnUrlTracker, KbnUrlTracker } from './kbn_url_tracker'; +import { BehaviorSubject, Subject } from 'rxjs'; +import { AppBase, ToastsSetup } from 'kibana/public'; +import { coreMock } from '../../../../../core/public/mocks'; +import { unhashUrl } from './hash_unhash_url'; + +jest.mock('./hash_unhash_url', () => ({ + unhashUrl: jest.fn(x => x), +})); + +describe('kbnUrlTracker', () => { + let storage: StubBrowserStorage; + let history: History; + let urlTracker: KbnUrlTracker; + let state1Subject: Subject<{ key1: string }>; + let state2Subject: Subject<{ key2: string }>; + let navLinkUpdaterSubject: BehaviorSubject<(app: AppBase) => { activeUrl?: string } | undefined>; + let toastService: jest.Mocked; + + function createTracker() { + urlTracker = createKbnUrlTracker({ + baseUrl: '/app/test', + defaultSubUrl: '#/start', + storageKey: 'storageKey', + history, + storage, + stateParams: [ + { + kbnUrlKey: 'state1', + stateUpdate$: state1Subject.asObservable(), + }, + { + kbnUrlKey: 'state2', + stateUpdate$: state2Subject.asObservable(), + }, + ], + navLinkUpdater$: navLinkUpdaterSubject, + toastNotifications: toastService, + }); + } + + function getActiveNavLinkUrl() { + return navLinkUpdaterSubject.getValue()({} as AppBase)?.activeUrl; + } + + beforeEach(() => { + jest.clearAllMocks(); + toastService = coreMock.createSetup().notifications.toasts; + storage = new StubBrowserStorage(); + history = createMemoryHistory(); + state1Subject = new Subject<{ key1: string }>(); + state2Subject = new Subject<{ key2: string }>(); + navLinkUpdaterSubject = new BehaviorSubject< + (app: AppBase) => { activeUrl?: string } | undefined + >(() => undefined); + }); + + test('do not touch nav link to default if nothing else is set', () => { + createTracker(); + expect(getActiveNavLinkUrl()).toEqual(undefined); + }); + + test('set nav link to session storage value if defined', () => { + storage.setItem('storageKey', '#/deep/path'); + createTracker(); + expect(getActiveNavLinkUrl()).toEqual('/app/test#/deep/path'); + }); + + test('set nav link to default if app gets mounted', () => { + storage.setItem('storageKey', '#/deep/path'); + createTracker(); + urlTracker.appMounted(); + expect(getActiveNavLinkUrl()).toEqual('/app/test#/start'); + }); + + test('keep nav link to default if path gets changed while app mounted', () => { + storage.setItem('storageKey', '#/deep/path'); + createTracker(); + urlTracker.appMounted(); + history.push('/deep/path/2'); + expect(getActiveNavLinkUrl()).toEqual('/app/test#/start'); + }); + + test('change nav link to last visited url within app after unmount', () => { + createTracker(); + urlTracker.appMounted(); + history.push('/deep/path/2'); + history.push('/deep/path/3'); + urlTracker.appUnMounted(); + expect(getActiveNavLinkUrl()).toEqual('/app/test#/deep/path/3'); + }); + + test('unhash all urls that are recorded while app is mounted', () => { + (unhashUrl as jest.Mock).mockImplementation(x => x + '?unhashed'); + createTracker(); + urlTracker.appMounted(); + history.push('/deep/path/2'); + history.push('/deep/path/3'); + urlTracker.appUnMounted(); + expect(unhashUrl).toHaveBeenCalledTimes(2); + expect(getActiveNavLinkUrl()).toEqual('/app/test#/deep/path/3?unhashed'); + }); + + test('show warning and use hashed url if unhashing does not work', () => { + (unhashUrl as jest.Mock).mockImplementation(() => { + throw new Error('unhash broke'); + }); + createTracker(); + urlTracker.appMounted(); + history.push('/deep/path/2'); + urlTracker.appUnMounted(); + expect(getActiveNavLinkUrl()).toEqual('/app/test#/deep/path/2'); + expect(toastService.addDanger).toHaveBeenCalledWith('unhash broke'); + }); + + test('change nav link back to default if app gets mounted again', () => { + createTracker(); + urlTracker.appMounted(); + history.push('/deep/path/2'); + history.push('/deep/path/3'); + urlTracker.appUnMounted(); + urlTracker.appMounted(); + expect(getActiveNavLinkUrl()).toEqual('/app/test#/start'); + }); + + test('update state param when app is not mounted', () => { + createTracker(); + state1Subject.next({ key1: 'abc' }); + expect(getActiveNavLinkUrl()).toMatchInlineSnapshot(`"/app/test#/start?state1=(key1:abc)"`); + }); + + test('update state param without overwriting rest of the url when app is not mounted', () => { + storage.setItem('storageKey', '#/deep/path?extrastate=1'); + createTracker(); + state1Subject.next({ key1: 'abc' }); + expect(getActiveNavLinkUrl()).toMatchInlineSnapshot( + `"/app/test#/deep/path?extrastate=1&state1=(key1:abc)"` + ); + }); + + test('not update state param when app is mounted', () => { + createTracker(); + urlTracker.appMounted(); + state1Subject.next({ key1: 'abc' }); + expect(getActiveNavLinkUrl()).toEqual('/app/test#/start'); + }); + + test('update state param multiple times when app is not mounted', () => { + createTracker(); + state1Subject.next({ key1: 'abc' }); + state1Subject.next({ key1: 'def' }); + expect(getActiveNavLinkUrl()).toMatchInlineSnapshot(`"/app/test#/start?state1=(key1:def)"`); + }); + + test('update multiple state params when app is not mounted', () => { + createTracker(); + state1Subject.next({ key1: 'abc' }); + state2Subject.next({ key2: 'def' }); + expect(getActiveNavLinkUrl()).toMatchInlineSnapshot( + `"/app/test#/start?state1=(key1:abc)&state2=(key2:def)"` + ); + }); +}); diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts new file mode 100644 index 0000000000000..6f3f64ea7b941 --- /dev/null +++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts @@ -0,0 +1,192 @@ +/* + * 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 { createHashHistory, History, UnregisterCallback } from 'history'; +import { BehaviorSubject, Observable, Subscription } from 'rxjs'; +import { AppBase, ToastsSetup } from 'kibana/public'; +import { setStateToKbnUrl } from './kbn_url_storage'; +import { unhashUrl } from './hash_unhash_url'; + +export interface KbnUrlTracker { + /** + * Callback to invoke when the app is mounted + */ + appMounted: () => void; + /** + * Callback to invoke when the app is unmounted + */ + appUnMounted: () => void; + /** + * Unregistering the url tracker. This won't reset the current state of the nav link + */ + stop: () => void; +} + +/** + * Listens to history changes and optionally to global state changes and updates the nav link url of + * a given app to point to the last visited page within the app. + * + * This includes the following parts: + * * When the app is currently active, the nav link points to the configurable default url of the app. + * * When the app is not active the last visited url is set to the nav link. + * * When a provided observable emits a new value, the state parameter in the url of the nav link is updated + * as long as the app is not active. + */ +export function createKbnUrlTracker({ + baseUrl, + defaultSubUrl, + storageKey, + stateParams, + navLinkUpdater$, + toastNotifications, + history, + storage, +}: { + /** + * Base url of the current app. This will be used as a prefix for the + * nav link in the side bar + */ + baseUrl: string; + /** + * Default sub url for this app. If the app is currently active or no sub url is already stored in session storage and the app hasn't been visited yet, the nav link will be set to this url. + */ + defaultSubUrl: string; + /** + * List of URL mapped states that should get updated even when the app is not currently active + */ + stateParams: Array<{ + /** + * Key of the query parameter containing the state + */ + kbnUrlKey: string; + /** + * Observable providing updates to the state + */ + stateUpdate$: Observable; + }>; + /** + * Key used to store the current sub url in session storage. This key should only be used for one active url tracker at any given ntime. + */ + storageKey: string; + /** + * App updater subject passed into the application definition to change nav link url. + */ + navLinkUpdater$: BehaviorSubject<(app: AppBase) => { activeUrl?: string } | undefined>; + /** + * Toast notifications service to show toasts in error cases. + */ + toastNotifications: ToastsSetup; + /** + * History object to use to track url changes. If this isn't provided, a local history instance will be created. + */ + history?: History; + /** + * Storage object to use to persist currently active url. If this isn't provided, the browser wide session storage instance will be used. + */ + storage?: Storage; +}): KbnUrlTracker { + const historyInstance = history || createHashHistory(); + const storageInstance = storage || sessionStorage; + + // local state storing current listeners and active url + let activeUrl: string = ''; + let unsubscribeURLHistory: UnregisterCallback | undefined; + let unsubscribeGlobalState: Subscription[] | undefined; + + function setNavLink(hash: string) { + navLinkUpdater$.next(() => ({ activeUrl: baseUrl + hash })); + } + + function getActiveSubUrl(url: string) { + // remove baseUrl prefix (just storing the sub url part) + return url.substr(baseUrl.length); + } + + function unsubscribe() { + if (unsubscribeURLHistory) { + unsubscribeURLHistory(); + unsubscribeURLHistory = undefined; + } + + if (unsubscribeGlobalState) { + unsubscribeGlobalState.forEach(sub => sub.unsubscribe()); + unsubscribeGlobalState = undefined; + } + } + + function onMountApp() { + unsubscribe(); + // track current hash when within app + unsubscribeURLHistory = historyInstance.listen(location => { + const urlWithHashes = baseUrl + '#' + location.pathname + location.search; + let urlWithStates = ''; + try { + urlWithStates = unhashUrl(urlWithHashes); + } catch (e) { + toastNotifications.addDanger(e.message); + } + + activeUrl = getActiveSubUrl(urlWithStates || urlWithHashes); + storageInstance.setItem(storageKey, activeUrl); + }); + } + + function onUnmountApp() { + unsubscribe(); + // propagate state updates when in other apps + unsubscribeGlobalState = stateParams.map(({ stateUpdate$, kbnUrlKey }) => + stateUpdate$.subscribe(state => { + const updatedUrl = setStateToKbnUrl( + kbnUrlKey, + state, + { useHash: false }, + baseUrl + (activeUrl || defaultSubUrl) + ); + // remove baseUrl prefix (just storing the sub url part) + activeUrl = getActiveSubUrl(updatedUrl); + storageInstance.setItem(storageKey, activeUrl); + setNavLink(activeUrl); + }) + ); + } + + // register listeners for unmounted app initially + onUnmountApp(); + + // initialize nav link and internal state + const storedUrl = storageInstance.getItem(storageKey); + if (storedUrl) { + activeUrl = storedUrl; + setNavLink(storedUrl); + } + + return { + appMounted() { + onMountApp(); + setNavLink(defaultSubUrl); + }, + appUnMounted() { + onUnmountApp(); + setNavLink(activeUrl); + }, + stop() { + unsubscribe(); + }, + }; +} diff --git a/src/plugins/ui_actions/public/actions/i_action.ts b/src/plugins/ui_actions/public/actions/i_action.ts index 20fdda9033f6a..544b66b26c974 100644 --- a/src/plugins/ui_actions/public/actions/i_action.ts +++ b/src/plugins/ui_actions/public/actions/i_action.ts @@ -17,6 +17,8 @@ * under the License. */ +import { UiComponent } from 'src/plugins/kibana_utils/common'; + export interface IAction { /** * Determined the order when there is more than one action matched to a trigger. @@ -39,6 +41,12 @@ export interface IAction { */ getDisplayName(context: ActionContext): string; + /** + * `UiComponent` to render when displaying this action as a context menu item. + * If not provided, `getDisplayName` will be used instead. + */ + MenuItem?: UiComponent<{ context: ActionContext }>; + /** * Returns a promise that resolves to true if this action is compatible given the context, * otherwise resolves to false. diff --git a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.ts b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx similarity index 89% rename from src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.ts rename to src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx index 8de447f5acf9c..3b76ff66f3aea 100644 --- a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.ts +++ b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx @@ -17,9 +17,11 @@ * under the License. */ +import * as React from 'react'; import { EuiContextMenuPanelDescriptor, EuiContextMenuPanelItemDescriptor } from '@elastic/eui'; import _ from 'lodash'; import { i18n } from '@kbn/i18n'; +import { uiToReactComponent } from '../../../kibana_react/public'; import { IAction } from '../actions'; /** @@ -98,7 +100,12 @@ function convertPanelActionToContextMenuItem({ closeMenu: () => void; }): EuiContextMenuPanelItemDescriptor { const menuPanelItem: EuiContextMenuPanelItemDescriptor = { - name: action.getDisplayName(actionContext), + name: action.MenuItem + ? // Cast to `any` because `name` typed to string. + (React.createElement(uiToReactComponent(action.MenuItem), { + context: actionContext, + }) as any) + : action.getDisplayName(actionContext), icon: action.getIconType(actionContext), panel: _.get(action, 'childContextMenuPanel.id'), 'data-test-subj': `embeddablePanelAction-${action.id}`, diff --git a/src/plugins/ui_actions/public/tests/test_samples/hello_world_action.tsx b/src/plugins/ui_actions/public/tests/test_samples/hello_world_action.tsx index 9e3d206c9a6dc..4e18b8ec27fb9 100644 --- a/src/plugins/ui_actions/public/tests/test_samples/hello_world_action.tsx +++ b/src/plugins/ui_actions/public/tests/test_samples/hello_world_action.tsx @@ -18,16 +18,31 @@ */ import React from 'react'; -import { EuiFlyout } from '@elastic/eui'; +import { EuiFlyout, EuiFlexGroup, EuiFlexItem, EuiBadge } from '@elastic/eui'; import { CoreStart } from 'src/core/public'; import { createAction, IAction } from '../../actions'; -import { toMountPoint } from '../../../../kibana_react/public'; +import { toMountPoint, reactToUiComponent } from '../../../../kibana_react/public'; + +const ReactMenuItem: React.FC = () => { + return ( + + Hello world! + + {'secret'} + + + ); +}; + +const UiMenuItem = reactToUiComponent(ReactMenuItem); export const HELLO_WORLD_ACTION_ID = 'HELLO_WORLD_ACTION_ID'; export function createHelloWorldAction(overlays: CoreStart['overlays']): IAction { return createAction({ type: HELLO_WORLD_ACTION_ID, + getIconType: () => 'lock', + MenuItem: UiMenuItem, execute: async () => { const flyoutSession = overlays.openFlyout( toMountPoint( diff --git a/src/plugins/usage_collection/server/collector/collector.js b/src/plugins/usage_collection/server/collector/collector.ts similarity index 59% rename from src/plugins/usage_collection/server/collector/collector.js rename to src/plugins/usage_collection/server/collector/collector.ts index 54d18ec2b8a7f..e102dc2a64ee8 100644 --- a/src/plugins/usage_collection/server/collector/collector.js +++ b/src/plugins/usage_collection/server/collector/collector.ts @@ -17,7 +17,30 @@ * under the License. */ -export class Collector { +import { Logger } from 'kibana/server'; +import { CallCluster } from 'src/legacy/core_plugins/elasticsearch'; + +export type CollectorFormatForBulkUpload = (result: T) => { type: string; payload: U }; + +export interface CollectorOptions { + type: string; + init?: Function; + fetch: (callCluster: CallCluster) => Promise | T; + /* + * A hook for allowing the fetched data payload to be organized into a typed + * data model for internal bulk upload. See defaultFormatterForBulkUpload for + * a generic example. + */ + formatForBulkUpload?: CollectorFormatForBulkUpload; + isReady: () => Promise | boolean; +} + +export class Collector { + public readonly type: CollectorOptions['type']; + public readonly init?: CollectorOptions['init']; + public readonly fetch: CollectorOptions['fetch']; + private readonly _formatForBulkUpload?: CollectorFormatForBulkUpload; + public readonly isReady: CollectorOptions['isReady']; /* * @param {Object} logger - logger object * @param {String} options.type - property name as the key for the data @@ -27,8 +50,8 @@ export class Collector { * @param {Function} options.rest - optional other properties */ constructor( - logger, - { type, init, fetch, formatForBulkUpload = null, isReady = null, ...options } = {} + protected readonly log: Logger, + { type, init, fetch, formatForBulkUpload, isReady, ...options }: CollectorOptions ) { if (type === undefined) { throw new Error('Collector must be instantiated with a options.type string property'); @@ -42,41 +65,27 @@ export class Collector { throw new Error('Collector must be instantiated with a options.fetch function property'); } - this.log = logger; - Object.assign(this, options); // spread in other properties and mutate "this" this.type = type; this.init = init; this.fetch = fetch; - - const defaultFormatterForBulkUpload = result => ({ type, payload: result }); - this._formatForBulkUpload = formatForBulkUpload || defaultFormatterForBulkUpload; - if (typeof isReady === 'function') { - this.isReady = isReady; - } + this.isReady = typeof isReady === 'function' ? isReady : () => true; + this._formatForBulkUpload = formatForBulkUpload; } - /* - * @param {Function} callCluster - callCluster function - */ - fetchInternal(callCluster) { - if (typeof callCluster !== 'function') { - throw new Error('A `callCluster` function must be passed to the fetch methods of collectors'); + public formatForBulkUpload(result: T) { + if (this._formatForBulkUpload) { + return this._formatForBulkUpload(result); + } else { + return this.defaultFormatterForBulkUpload(result); } - return this.fetch(callCluster); - } - - /* - * A hook for allowing the fetched data payload to be organized into a typed - * data model for internal bulk upload. See defaultFormatterForBulkUpload for - * a generic example. - */ - formatForBulkUpload(result) { - return this._formatForBulkUpload(result); } - isReady() { - throw `isReady() must be implemented in ${this.type} collector`; + protected defaultFormatterForBulkUpload(result: T) { + return { + type: this.type, + payload: result, + }; } } diff --git a/src/plugins/usage_collection/server/collector/__tests__/collector_set.js b/src/plugins/usage_collection/server/collector/collector_set.test.ts similarity index 53% rename from src/plugins/usage_collection/server/collector/__tests__/collector_set.js rename to src/plugins/usage_collection/server/collector/collector_set.test.ts index 397499650e054..c85880c34d72b 100644 --- a/src/plugins/usage_collection/server/collector/__tests__/collector_set.js +++ b/src/plugins/usage_collection/server/collector/collector_set.test.ts @@ -18,58 +18,62 @@ */ import { noop } from 'lodash'; -import sinon from 'sinon'; -import expect from '@kbn/expect'; -import { Collector } from '../collector'; -import { CollectorSet } from '../collector_set'; -import { UsageCollector } from '../usage_collector'; - -const mockLogger = () => ({ - debug: sinon.spy(), - warn: sinon.spy(), -}); +import { Collector } from './collector'; +import { CollectorSet } from './collector_set'; +import { UsageCollector } from './usage_collector'; +import { loggingServiceMock } from '../../../../core/server/mocks'; + +const logger = loggingServiceMock.createLogger(); + +const loggerSpies = { + debug: jest.spyOn(logger, 'debug'), + warn: jest.spyOn(logger, 'warn'), +}; describe('CollectorSet', () => { describe('registers a collector set and runs lifecycle events', () => { - let init; - let fetch; + let init: Function; + let fetch: Function; beforeEach(() => { init = noop; fetch = noop; + loggerSpies.debug.mockRestore(); + loggerSpies.warn.mockRestore(); }); + const mockCallCluster = () => Promise.resolve({ passTest: 1000 }); + it('should throw an error if non-Collector type of object is registered', () => { - const logger = mockLogger(); const collectors = new CollectorSet({ logger }); const registerPojo = () => { collectors.registerCollector({ type: 'type_collector_test', init, fetch, - }); + } as any); // We are intentionally sending it wrong. }; - expect(registerPojo).to.throwException(({ message }) => { - expect(message).to.be('CollectorSet can only have Collector instances registered'); - }); + expect(registerPojo).toThrowError( + 'CollectorSet can only have Collector instances registered' + ); }); it('should log debug status of fetching from the collector', async () => { - const mockCallCluster = () => Promise.resolve({ passTest: 1000 }); - const logger = mockLogger(); const collectors = new CollectorSet({ logger }); collectors.registerCollector( new Collector(logger, { type: 'MY_TEST_COLLECTOR', - fetch: caller => caller(), + fetch: (caller: any) => caller(), + isReady: () => true, }) ); - const result = await collectors.bulkFetch(mockCallCluster); - const calls = logger.debug.getCalls(); - expect(calls.length).to.be(1); - expect(calls[0].args).to.eql(['Fetching data from MY_TEST_COLLECTOR collector']); - expect(result).to.eql([ + const result = await collectors.bulkFetch(mockCallCluster as any); + expect(loggerSpies.debug).toHaveBeenCalledTimes(1); + expect(loggerSpies.debug).toHaveBeenCalledWith( + 'Fetching data from MY_TEST_COLLECTOR collector' + ); + expect(result).toStrictEqual([ { type: 'MY_TEST_COLLECTOR', result: { passTest: 1000 }, @@ -78,32 +82,90 @@ describe('CollectorSet', () => { }); it('should gracefully handle a collector fetch method throwing an error', async () => { - const mockCallCluster = () => Promise.resolve({ passTest: 1000 }); - const logger = mockLogger(); const collectors = new CollectorSet({ logger }); collectors.registerCollector( new Collector(logger, { type: 'MY_TEST_COLLECTOR', fetch: () => new Promise((_resolve, reject) => reject()), + isReady: () => true, }) ); let result; try { - result = await collectors.bulkFetch(mockCallCluster); + result = await collectors.bulkFetch(mockCallCluster as any); } catch (err) { // Do nothing } // This must return an empty object instead of null/undefined - expect(result).to.eql([]); + expect(result).toStrictEqual([]); + }); + + it('should not break if isReady is not a function', async () => { + const collectors = new CollectorSet({ logger }); + collectors.registerCollector( + new Collector(logger, { + type: 'MY_TEST_COLLECTOR', + fetch: () => ({ test: 1 }), + isReady: true as any, + }) + ); + + const result = await collectors.bulkFetch(mockCallCluster as any); + expect(result).toStrictEqual([ + { + type: 'MY_TEST_COLLECTOR', + result: { test: 1 }, + }, + ]); + }); + + it('should not break if isReady is not provided', async () => { + const collectors = new CollectorSet({ logger }); + collectors.registerCollector( + new Collector(logger, { + type: 'MY_TEST_COLLECTOR', + fetch: () => ({ test: 1 }), + } as any) + ); + + const result = await collectors.bulkFetch(mockCallCluster as any); + expect(result).toStrictEqual([ + { + type: 'MY_TEST_COLLECTOR', + result: { test: 1 }, + }, + ]); + }); + + it('should infer the types from the implementations of fetch and formatForBulkUpload', async () => { + const collectors = new CollectorSet({ logger }); + collectors.registerCollector( + new Collector(logger, { + type: 'MY_TEST_COLLECTOR', + fetch: () => ({ test: 1 }), + formatForBulkUpload: result => ({ + type: 'MY_TEST_COLLECTOR', + payload: { test: result.test * 2 }, + }), + isReady: () => true, + }) + ); + + const result = await collectors.bulkFetch(mockCallCluster as any); + expect(result).toStrictEqual([ + { + type: 'MY_TEST_COLLECTOR', + result: { test: 1 }, // It matches the return of `fetch`. `formatForBulkUpload` is used later on + }, + ]); }); }); describe('toApiFieldNames', () => { - let collectorSet; + let collectorSet: CollectorSet; beforeEach(() => { - const logger = mockLogger(); collectorSet = new CollectorSet({ logger }); }); @@ -126,7 +188,7 @@ describe('CollectorSet', () => { }; const result = collectorSet.toApiFieldNames(apiData); - expect(result).to.eql({ + expect(result).toStrictEqual({ os: { load: { '15m': 2.3525390625, '1m': 2.22412109375, '5m': 2.4462890625 }, memory: { free_bytes: 458280960, total_bytes: 17179869184, used_bytes: 16721588224 }, @@ -155,7 +217,7 @@ describe('CollectorSet', () => { }; const result = collectorSet.toApiFieldNames(apiData); - expect(result).to.eql({ + expect(result).toStrictEqual({ days_of_the_week: [ { day_index: 1, day_name: 'monday' }, { day_index: 2, day_name: 'tuesday' }, @@ -166,21 +228,20 @@ describe('CollectorSet', () => { }); describe('isUsageCollector', () => { - const collectorOptions = { type: 'MY_TEST_COLLECTOR', fetch: () => {} }; + const collectorOptions = { type: 'MY_TEST_COLLECTOR', fetch: () => {}, isReady: () => true }; it('returns true only for UsageCollector instances', () => { - const logger = mockLogger(); const collectors = new CollectorSet({ logger }); const usageCollector = new UsageCollector(logger, collectorOptions); const collector = new Collector(logger, collectorOptions); const randomClass = new (class Random {})(); - expect(collectors.isUsageCollector(usageCollector)).to.be(true); - expect(collectors.isUsageCollector(collector)).to.be(false); - expect(collectors.isUsageCollector(randomClass)).to.be(false); - expect(collectors.isUsageCollector({})).to.be(false); - expect(collectors.isUsageCollector(null)).to.be(false); - expect(collectors.isUsageCollector('')).to.be(false); - expect(collectors.isUsageCollector()).to.be(false); + expect(collectors.isUsageCollector(usageCollector)).toEqual(true); + expect(collectors.isUsageCollector(collector)).toEqual(false); + expect(collectors.isUsageCollector(randomClass)).toEqual(false); + expect(collectors.isUsageCollector({})).toEqual(false); + expect(collectors.isUsageCollector(null)).toEqual(false); + expect(collectors.isUsageCollector('')).toEqual(false); + expect(collectors.isUsageCollector(void 0)).toEqual(false); }); }); }); diff --git a/src/plugins/usage_collection/server/collector/collector_set.ts b/src/plugins/usage_collection/server/collector/collector_set.ts index a87accc47535e..6cc5d057b080a 100644 --- a/src/plugins/usage_collection/server/collector/collector_set.ts +++ b/src/plugins/usage_collection/server/collector/collector_set.ts @@ -20,39 +20,37 @@ import { snakeCase } from 'lodash'; import { Logger } from 'kibana/server'; import { CallCluster } from 'src/legacy/core_plugins/elasticsearch'; -// @ts-ignore -import { Collector } from './collector'; -// @ts-ignore +import { Collector, CollectorOptions } from './collector'; import { UsageCollector } from './usage_collector'; interface CollectorSetConfig { logger: Logger; - maximumWaitTimeForAllCollectorsInS: number; - collectors?: Collector[]; + maximumWaitTimeForAllCollectorsInS?: number; + collectors?: Array>; } export class CollectorSet { private _waitingForAllCollectorsTimestamp?: number; private logger: Logger; private readonly maximumWaitTimeForAllCollectorsInS: number; - private collectors: Collector[] = []; + private collectors: Array> = []; constructor({ logger, maximumWaitTimeForAllCollectorsInS, collectors = [] }: CollectorSetConfig) { this.logger = logger; this.collectors = collectors; this.maximumWaitTimeForAllCollectorsInS = maximumWaitTimeForAllCollectorsInS || 60; } - public makeStatsCollector = (options: any) => { + public makeStatsCollector = (options: CollectorOptions) => { return new Collector(this.logger, options); }; - public makeUsageCollector = (options: any) => { + public makeUsageCollector = (options: CollectorOptions) => { return new UsageCollector(this.logger, options); }; /* * @param collector {Collector} collector object */ - public registerCollector = (collector: Collector) => { + public registerCollector = (collector: Collector) => { // check instanceof if (!(collector instanceof Collector)) { throw new Error('CollectorSet can only have Collector instances registered'); @@ -115,7 +113,7 @@ export class CollectorSet { public bulkFetch = async ( callCluster: CallCluster, - collectors: Collector[] = this.collectors + collectors: Array> = this.collectors ) => { const responses = []; for (const collector of collectors) { @@ -123,7 +121,7 @@ export class CollectorSet { try { responses.push({ type: collector.type, - result: await collector.fetchInternal(callCluster), + result: await collector.fetch(callCluster), }); } catch (err) { this.logger.warn(err); @@ -148,14 +146,13 @@ export class CollectorSet { }; // convert an array of fetched stats results into key/object - public toObject = (statsData: any) => { - if (!statsData) return {}; - return statsData.reduce((accumulatedStats: any, { type, result }: any) => { + public toObject = (statsData: Array<{ type: string; result: T }> = []) => { + return statsData.reduce((accumulatedStats, { type, result }) => { return { ...accumulatedStats, [type]: result, }; - }, {}); + }, {} as Result); }; // rename fields to use api conventions diff --git a/src/plugins/usage_collection/server/collector/index.ts b/src/plugins/usage_collection/server/collector/index.ts index 962f61474c250..0d3939e1dc681 100644 --- a/src/plugins/usage_collection/server/collector/index.ts +++ b/src/plugins/usage_collection/server/collector/index.ts @@ -18,7 +18,5 @@ */ export { CollectorSet } from './collector_set'; -// @ts-ignore export { Collector } from './collector'; -// @ts-ignore export { UsageCollector } from './usage_collector'; diff --git a/src/plugins/usage_collection/server/collector/usage_collector.js b/src/plugins/usage_collection/server/collector/usage_collector.js deleted file mode 100644 index 54863474dbd01..0000000000000 --- a/src/plugins/usage_collection/server/collector/usage_collector.js +++ /dev/null @@ -1,51 +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 { KIBANA_STATS_TYPE } from '../../common/constants'; -import { Collector } from './collector'; - -export class UsageCollector extends Collector { - /* - * @param {Object} logger - logger object - * @param {String} options.type - property name as the key for the data - * @param {Function} options.init (optional) - initialization function - * @param {Function} options.fetch - function to query data - * @param {Function} options.formatForBulkUpload - optional - * @param {Function} options.rest - optional other properties - */ - constructor(logger, { type, init, fetch, formatForBulkUpload = null, ...options } = {}) { - super(logger, { type, init, fetch, formatForBulkUpload, ...options }); - - /* - * Currently, for internal bulk uploading, usage stats are part of - * `kibana_stats` type, under the `usage` namespace in the document. - */ - const defaultUsageFormatterForBulkUpload = result => { - return { - type: KIBANA_STATS_TYPE, - payload: { - usage: { - [type]: result, - }, - }, - }; - }; - this._formatForBulkUpload = formatForBulkUpload || defaultUsageFormatterForBulkUpload; - } -} diff --git a/src/plugins/usage_collection/server/collector/usage_collector.ts b/src/plugins/usage_collection/server/collector/usage_collector.ts new file mode 100644 index 0000000000000..05c701bd3abf4 --- /dev/null +++ b/src/plugins/usage_collection/server/collector/usage_collector.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. + */ + +import { KIBANA_STATS_TYPE } from '../../common/constants'; +import { Collector } from './collector'; + +export class UsageCollector extends Collector< + T, + U +> { + protected defaultUsageFormatterForBulkUpload(result: T) { + return { + type: KIBANA_STATS_TYPE, + payload: { + usage: { + [this.type]: result, + }, + }, + }; + } +} diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client_factory.test.ts b/x-pack/legacy/plugins/alerting/server/alerts_client_factory.test.ts index 754e02a3f1e5e..14c685237bf92 100644 --- a/x-pack/legacy/plugins/alerting/server/alerts_client_factory.test.ts +++ b/x-pack/legacy/plugins/alerting/server/alerts_client_factory.test.ts @@ -86,7 +86,7 @@ test('getUserName() returns a name when security is enabled', async () => { factory.create(KibanaRequest.from(fakeRequest), fakeRequest); const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0]; - securityPluginSetup.authc.getCurrentUser.mockResolvedValueOnce({ username: 'bob' }); + securityPluginSetup.authc.getCurrentUser.mockReturnValueOnce({ username: 'bob' }); const userNameResult = await constructorCall.getUserName(); expect(userNameResult).toEqual('bob'); }); diff --git a/x-pack/legacy/plugins/apm/common/projections/typings.ts b/x-pack/legacy/plugins/apm/common/projections/typings.ts index 2b55395b70c6b..08a7bee5412a5 100644 --- a/x-pack/legacy/plugins/apm/common/projections/typings.ts +++ b/x-pack/legacy/plugins/apm/common/projections/typings.ts @@ -4,11 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ESSearchRequest, ESSearchBody } from '../../typings/elasticsearch'; +import { + ESSearchRequest, + ESSearchBody +} from '../../../../../plugins/apm/typings/elasticsearch'; import { AggregationOptionsByType, AggregationInputMap -} from '../../typings/elasticsearch/aggregations'; +} from '../../../../../plugins/apm/typings/elasticsearch/aggregations'; export type Projection = Omit & { body: Omit & { diff --git a/x-pack/legacy/plugins/apm/common/projections/util/merge_projection/index.ts b/x-pack/legacy/plugins/apm/common/projections/util/merge_projection/index.ts index 6a5089733bb33..ef6089872b786 100644 --- a/x-pack/legacy/plugins/apm/common/projections/util/merge_projection/index.ts +++ b/x-pack/legacy/plugins/apm/common/projections/util/merge_projection/index.ts @@ -5,11 +5,11 @@ */ import { merge, isPlainObject, cloneDeep } from 'lodash'; import { DeepPartial } from 'utility-types'; -import { AggregationInputMap } from '../../../../typings/elasticsearch/aggregations'; +import { AggregationInputMap } from '../../../../../../../plugins/apm/typings/elasticsearch/aggregations'; import { ESSearchRequest, ESSearchBody -} from '../../../../typings/elasticsearch'; +} from '../../../../../../../plugins/apm/typings/elasticsearch'; import { Projection } from '../../typings'; type PlainObject = Record; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Controls.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Controls.tsx index 07ea97f442b7f..38e86e4a0d1c9 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Controls.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Controls.tsx @@ -11,7 +11,6 @@ import React, { useContext, useEffect, useState } from 'react'; import styled from 'styled-components'; import { CytoscapeContext } from './Cytoscape'; import { animationOptions, nodeHeight } from './cytoscapeOptions'; -import { FullscreenPanel } from './FullscreenPanel'; const ControlsContainer = styled('div')` left: ${theme.gutterTypes.gutterMedium}; @@ -87,7 +86,6 @@ export function Controls() { const minZoom = cy.minZoom(); const isMinZoom = zoom === minZoom; const increment = (maxZoom - minZoom) / steps; - const mapDomElement = cy.container(); const zoomInLabel = i18n.translate('xpack.apm.serviceMap.zoomIn', { defaultMessage: 'Zoom in' }); @@ -127,7 +125,6 @@ export function Controls() { title={centerLabel} /> - ); } diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/FullscreenPanel.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/FullscreenPanel.tsx deleted file mode 100644 index 851bf0ebf56fd..0000000000000 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/FullscreenPanel.tsx +++ /dev/null @@ -1,57 +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 React from 'react'; -import { EuiButtonIcon, EuiPanel } from '@elastic/eui'; -import styled from 'styled-components'; -import theme from '@elastic/eui/dist/eui_theme_light.json'; -import { i18n } from '@kbn/i18n'; - -const Button = styled(EuiButtonIcon)` - display: block; - margin: ${theme.paddingSizes.xs}; -`; - -interface FullscreenPanelProps { - element: Element | null; -} - -export function FullscreenPanel({ element }: FullscreenPanelProps) { - const canDoFullscreen = - element && element.ownerDocument && element.ownerDocument.fullscreenEnabled; - - if (!canDoFullscreen) { - return null; - } - - function doFullscreen() { - if (element && element.ownerDocument && canDoFullscreen) { - const isFullscreen = element.ownerDocument.fullscreenElement !== null; - - if (isFullscreen) { - element.ownerDocument.exitFullscreen(); - } else { - element.requestFullscreen(); - } - } - } - - const label = i18n.translate('xpack.apm.serviceMap.fullscreen', { - defaultMessage: 'Full screen' - }); - - return ( - -